{"id":23,"date":"2026-09-16T10:28:32","date_gmt":"2026-09-16T10:28:32","guid":{"rendered":"https:\/\/docs.latw.ai\/uncategorized\/styling\/"},"modified":"2026-09-16T10:28:32","modified_gmt":"2026-09-16T10:28:32","slug":"styling","status":"publish","type":"post","link":"https:\/\/docs.latw.ai\/pl\/esti-to-wordpress\/styling\/","title":{"rendered":"Styling"},"content":{"rendered":"<h1>Styling without touching templates<\/h1>\n<p>The plugin ships one stylesheet, <code>assets\/css\/frontend.css<\/code>, enqueued as <code>osfec-frontend<\/code>. It loads on the offer archive,<br \/>\non single offers, and wherever the shortcode or the Offers block appears.<\/p>\n<p>Its design intent: <strong>inherit the theme, impose nothing.<\/strong> Colours, widths and typefaces come from the theme&#8217;s global<br \/>\nstyles, so on a block theme the offer pages already look like the rest of the site. Each value carries a fallback for<br \/>\nclassic themes that expose no global styles.<\/p>\n<p>Most of what people want to change is reachable through custom properties, without overriding a single rule.<\/p>\n<h2>Custom properties<\/h2>\n<p>They are declared on the <code>.osfec<\/code> wrapper, which every component the plugin renders carries.<\/p>\n<table>\n<thead>\n<tr>\n<th>Property<\/th>\n<th>Falls back to<\/th>\n<th>Used for<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>--osfec-ink<\/code><\/td>\n<td>theme <code>contrast<\/code>, <code>#111111<\/code><\/td>\n<td>Body text<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-muted<\/code><\/td>\n<td>theme <code>accent-4<\/code>, <code>#686868<\/code><\/td>\n<td>Labels, secondary text<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-soft<\/code><\/td>\n<td>theme <code>accent-5<\/code>, <code>#f7f7f5<\/code><\/td>\n<td>Subtle backgrounds, badges<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-surface<\/code><\/td>\n<td>theme <code>base<\/code>, <code>#ffffff<\/code><\/td>\n<td>Cards and panels<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-line<\/code><\/td>\n<td>theme <code>accent-6<\/code>, <code>rgba(17,17,17,.18)<\/code><\/td>\n<td>Borders and separators<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-accent<\/code><\/td>\n<td>theme <code>contrast<\/code>, <code>#111111<\/code><\/td>\n<td>Buttons, active states<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-on-accent<\/code><\/td>\n<td>theme <code>base<\/code>, <code>#ffffff<\/code><\/td>\n<td>Text on the accent colour<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-wide<\/code><\/td>\n<td>theme wide size, <code>1340px<\/code><\/td>\n<td>Maximum width of a component<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-content<\/code><\/td>\n<td>theme content size, <code>645px<\/code><\/td>\n<td>Reading width of the description<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-gutter<\/code><\/td>\n<td>theme spacing preset 50, <code>1.5rem<\/code><\/td>\n<td>Horizontal padding<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-radius<\/code><\/td>\n<td><code>16px<\/code><\/td>\n<td>Corner radius of cards and panels<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-radius-sm<\/code><\/td>\n<td><code>10px<\/code><\/td>\n<td>Corner radius of inputs and small elements<\/td>\n<\/tr>\n<tr>\n<td><code>--osfec-columns<\/code><\/td>\n<td><code>3<\/code><\/td>\n<td>Grid columns, set by the <code>columns<\/code> attribute<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Rebranding in a few lines<\/h2>\n<p>Put this in <strong>Appearance \u2192 Customize \u2192 Additional CSS<\/strong>, or in your child theme&#8217;s stylesheet. Target <code>.osfec<\/code>, which is<br \/>\nwhere the properties are declared:<\/p>\n<pre><code class=\"language-css\">.osfec {\n    --osfec-accent: #0f5132;\n    --osfec-on-accent: #ffffff;\n    --osfec-soft: #f2f7f4;\n    --osfec-line: rgba(15, 81, 50, .2);\n    --osfec-radius: 4px;\n    --osfec-radius-sm: 4px;\n}<\/code><\/pre>\n<p>Narrower offer pages, without touching the theme&#8217;s own width:<\/p>\n<pre><code class=\"language-css\">.osfec {\n    --osfec-wide: 1100px;\n    --osfec-content: 620px;\n}<\/code><\/pre>\n<p>Dark cards on a dark theme:<\/p>\n<pre><code class=\"language-css\">.osfec {\n    --osfec-surface: #16181c;\n    --osfec-soft: #1f2228;\n    --osfec-ink: #f2f2f2;\n    --osfec-muted: #a8adb5;\n    --osfec-line: rgba(255, 255, 255, .16);\n}<\/code><\/pre>\n<h2>Loading order and specificity<\/h2>\n<p>The plugin stylesheet is enqueued as <code>osfec-frontend<\/code>. A theme stylesheet usually loads before it, which means a plain<br \/>\n<code>.osfec-card { \u2026 }<\/code> in the theme can lose to the plugin&#8217;s own rule.<\/p>\n<p>Reliable ways round it, in order of preference:<\/p>\n<ol>\n<li><strong>Override the custom properties.<\/strong> They always win, because the plugin&#8217;s rules read them.<\/li>\n<li><strong>Add specificity<\/strong>: <code>.osfec .osfec-card { \u2026 }<\/code> beats the plugin&#8217;s single-class selector.<\/li>\n<li><strong>Enqueue your CSS after the plugin&#8217;s<\/strong>, declaring a dependency:<\/li>\n<\/ol>\n<pre><code class=\"language-php\">add_action( 'wp_enqueue_scripts', function () {\n    wp_enqueue_style(\n        'my-theme-offers',\n        get_stylesheet_directory_uri() . '\/offers.css',\n        array( 'osfec-frontend' ),\n        '1.0'\n    );\n}, 20 );<\/code><\/pre>\n<p><code>!important<\/code> is a last resort; the buttons already use <code>.osfec .osfec-button<\/code>, so a bare <code>.osfec-button<\/code> rule of yours<br \/>\nwill not win.<\/p>\n<h2>Dropping the plugin stylesheet entirely<\/h2>\n<p>If you are styling the offer components from scratch, dequeue it:<\/p>\n<pre><code class=\"language-php\">add_action( 'wp_enqueue_scripts', function () {\n    wp_dequeue_style( 'osfec-frontend' );\n}, 20 );<\/code><\/pre>\n<p>Two consequences: the layout is then entirely yours, including the gallery, whose slides are positioned by CSS \u2014 the<br \/>\nscript only moves the track and updates the counter.<\/p>\n<h2>Widths and alignment<\/h2>\n<p><code>.osfec<\/code> caps itself at the theme&#8217;s wide size plus a gutter on each side, so an offer page lines up with a wide-aligned<br \/>\nblock in the theme. The shortcode and block variant (<code>.osfec.osfec-offers<\/code>) drops its own side padding, on the<br \/>\nassumption that the section it is dropped into already brings one. If your list looks inset or flush by one gutter too<br \/>\nmany, that pair of rules is why.<\/p>\n<h2>Responsive breakpoints<\/h2>\n<p>Three breakpoints are built in and are worth matching in your own overrides:<\/p>\n<ul>\n<li><strong>980px<\/strong> \u2014 a grid of more than two columns drops to two<\/li>\n<li><strong>781px<\/strong> \u2014 the card switches to its compact layout<\/li>\n<li><strong>600px<\/strong> \u2014 the grid drops to a single column<\/li>\n<\/ul>\n<h2>What styling cannot change<\/h2>\n<ul>\n<li><strong>Photo dimensions.<\/strong> Images are hotlinked from the Esti CDN in the sizes Esti serves (<code>_max<\/code> for the slider, <code>_min<\/code><br \/>\nfor thumbnails). You can crop them with CSS, but you cannot generate new sizes.<\/li>\n<li><strong>Which fields appear.<\/strong> Empty fields are dropped before rendering, and dictionary-backed attributes are hidden while<br \/>\nunresolved. Changing what a page shows means overriding its template \u2014<br \/>\nsee <a href=\"\/esti-to-wordpress\/template-overrides\">Overriding templates<\/a>.<\/li>\n<li><strong>The markup itself.<\/strong> Also a template job. The class names it produces are in<br \/>\nthe <a href=\"\/esti-to-wordpress\/css-class-map\">CSS class map<\/a>.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Styling without touching templates The plugin ships one stylesheet, assets\/css\/frontend.css, enqueued as osfec-frontend. It loads on the offer archive, on single offers, and wherever the shortcode\u2026<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_latwm_translated_slug":"","footnotes":""},"categories":[2],"tags":[],"class_list":["post-23","post","type-post","status-publish","format-standard","hentry","category-esti-to-wordpress"],"_links":{"self":[{"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/posts\/23","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/comments?post=23"}],"version-history":[{"count":1,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/posts\/23\/revisions"}],"predecessor-version":[{"id":49,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/posts\/23\/revisions\/49"}],"wp:attachment":[{"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/media?parent=23"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/categories?post=23"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/docs.latw.ai\/pl\/wp-json\/wp\/v2\/tags?post=23"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}