[{"categories":null,"content":"Anchor positioning had me wondered for a while, however after reading some articles about it I think this is a great addition to modern css.\nEspecially in use with some other new css/html additions like the popover api. And now support for Anchor positioning has landed in Safari 26 use in production is near (FireFox has it under a flag).\nI really like the absolute relative positioning and the baked in fallback on smaller screens.\nWhat does it do? You can take an element like a button, link or image and use it as a trigger for a popup/over element.\nAnd a second element that needs a position relatively to this trigger element.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /* Define an anchor element */ .anchor { anchor-name: --btn-anchor; } /* Anchor a target element */ .target { position: absolute; position-anchor: --btn-anchor; } /* Position a target element */ .target { position-area: start end; } The anchored element is surrounded by a virtual grid of 9 spaces to use for positioning the second element. Here this grid is visualized with the logical names for each space.\nstart start start center start end center start anchor Popover heading popover content\ncenter end end start end center end end However it is also possible to use the dimensions of the anchored element to set the coordinates of the paired element. Like in this tooltip example.\n? Tooltip heading Tooltip content\nor combine it with the new appearance: base-select; for select elements. (Chrome, Edge and safari TP only)\nSelect a country Select a country Netherlands Belgium Germany Denmark Finland France ","permalink":"https://www.webstf.nl/posts/anchor-positioning/","tags":["css","html","front-end"],"title":"Anchor Positioning"},{"categories":null,"content":"CSS length units fall into two broad categories: absolute units, which map to fixed physical or reference measurements, and relative units, which resolve in relation to something else — a font metric, a parent element, the viewport, or a container.\nFor front-end use it is widely accepted to use rem as the base unit, personally I seldom use px. If I do, most of the time it is in ascpect-ratio. From the newer units lh has quickly become one of my favourites. For mobile there is the ambivalent dvh. From the oldies it is em to use for proportional padding or margin.\nAbsolute units Absolute units were part of CSS from the very beginning. All seven are defined in CSS1 (1996) and carried through CSS2 and into the current CSS Values and Units specification unchanged in meaning. They are anchored to real-world physical measures, with the pixel (px) defined as 1/96 of an inch — a reference unit from which all others are derived.\ncm One centimetre as defined by the SI system. Primarily used in print stylesheets. mm One tenth of a centimetre. Useful for fine-grained print measurements. in Inch. Equal to 96px, or 2.54cm. Print and high-resolution media. pt Point. 1/72 of an inch, or approximately 1.333px. The traditional unit of type size in print. pc Pica. 12 points, or 1/6 of an inch (16px). A larger print typographic unit. px Pixel. 1/96 of an inch in CSS terms — a reference pixel independent of the physical pixel density of the screen. The most commonly used absolute unit for screen work. Q Quarter millimetre. 0.25mm. Added in CSS Values Level 3 (around 2012, widely supported from 2019). Used in Japanese print typography where the Q is a conventional typographic unit. Relative units Relative units have been added in waves across the evolution of CSS, and span a much wider range of reference points. The earliest date to CSS1; the most recent arrived in 2023.\n% Percentage. CSS1. Always relative to some other value — typically the parent element\u0026rsquo;s corresponding dimension or the element\u0026rsquo;s own font size. em The computed font size of the element. CSS1. When used on font-size itself, resolves against the parent\u0026rsquo;s font size. ex The x-height of the element\u0026rsquo;s font — approximately the height of a lowercase \u0026ldquo;x\u0026rdquo;. CSS1. ch The width of the \u0026ldquo;0\u0026rdquo; (zero) character in the element\u0026rsquo;s font. CSS Values Level 3, widely supported from around 2014. Useful for sizing to a number of characters. rem Root em. The computed font size of the root element (\u0026lt;html\u0026gt;). CSS Values Level 3, widely supported from around 2013. Like em but without the compounding inheritance problem. vw 1% of the viewport width. CSS Values Level 3, widely supported from around 2013. vh 1% of the viewport height. CSS Values Level 3, widely supported from around 2013. vmin 1% of the smaller of vw or vh. CSS Values Level 3, widely supported from around 2013. vmax 1% of the larger of vw or vh. CSS Values Level 3, widely supported from around 2013. fr Fractional unit used in CSS Grid track sizing. Defined in CSS Grid Layout Level 1, widely supported from around 2017. Represents a fraction of the remaining space in a grid container. vb 1% of the viewport\u0026rsquo;s block axis size (height in horizontal writing modes). CSS Values Level 4, widely supported from around 2021. vi 1% of the viewport\u0026rsquo;s inline axis size (width in horizontal writing modes). CSS Values Level 4, widely supported from around 2021. svh svw svi svb svmin svmax Small viewport units. CSS Values Level 4, widely supported from 2022–2023. Sized to the smallest possible viewport — assumes dynamic browser UI (address bar etc.) is fully visible. lvh lvw lvi lvb lvmin lvmax Large viewport units. CSS Values Level 4, widely supported from 2022–2023. Sized to the largest possible viewport — assumes dynamic browser UI is fully retracted. dvh dvw dvi dvb dvmin dvmax Dynamic viewport units. CSS Values Level 4, widely supported from 2022–2023. Update in real time as the browser UI expands and retracts. The recommended replacement for vh in full-screen mobile layouts. ic The width of the CJK water ideograph (水, U+6C34) in the element\u0026rsquo;s font. CSS Values Level 4, widely supported from around 2022. The CJK counterpart to ch. lh The computed line-height of the element. CSS Values Level 4, widely supported from around 2023. rlh The computed line-height of the root element. CSS Values Level 4, widely supported from around 2023. cap The cap height of the element\u0026rsquo;s font — the approximate height of uppercase letters. CSS Values Level 4, widely supported from around 2023. rcap The cap height of the root element\u0026rsquo;s font. CSS Values Level 4, widely supported from around 2023. rch The width of the \u0026ldquo;0\u0026rdquo; character in the root element\u0026rsquo;s font. CSS Values Level 4, widely supported from around 2023. rex The x-height of the root element\u0026rsquo;s font. CSS Values Level 4, widely supported from around 2023. ric The width of the CJK water ideograph in the root element\u0026rsquo;s font. CSS Values Level 4, widely supported from around 2023. cqw 1% of a container query container\u0026rsquo;s inline size (width). CSS Containment Level 3, widely supported from around 2023. cqh 1% of a container query container\u0026rsquo;s block size (height). CSS Containment Level 3, widely supported from around 2023. cqi 1% of a container query container\u0026rsquo;s inline axis size. CSS Containment Level 3, widely supported from around 2023. cqb 1% of a container query container\u0026rsquo;s block axis size. CSS Containment Level 3, widely supported from around 2023. cqmin The smaller of cqi and cqb. CSS Containment Level 3, widely supported from around 2023. cqmax The larger of cqi and cqb. CSS Containment Level 3, widely supported from around 2023. ","permalink":"https://www.webstf.nl/posts/css-units/","tags":null,"title":"CSS units"},{"categories":null,"content":"This is a plea for the power of the cascade — the C in CSS. Too many times I\u0026rsquo;ve read rants against it, and most of the time it comes down to a lack of understanding.\nThe cascade is actually enormously helpful when it comes to styling, as long as you know what you are doing. There is no other frontend technique that lets you change the look and feel of an entire document with so little effort. The way the cascade works is very consistent and very logical, and now that @layer has been added to the mix, it is even easier to influence.\nEven component-based design systems can use the cascade to their advantage. A design system probably has more similarities than differences across its components — so style the rule, not the exception. And when exceptions do arise, just style those. It takes less code too.\nIs CSS a programming language? CSS is not considered a programming language, and maybe it is not one, which is not a bad thing. That does not make it without logic, however — just not conventional programming logic. There are no user-defined functions, no loops. If statements are being worked on and even media queries are a form of if statement anyway. Built-in functions have made great progress in recent years and there is a lot to choose from, especially when it comes to colour.\nAll of that aside, CSS is first and foremost about layout, styling, and visual appeal. It is pretty harmless — yet remarkably powerful, in that a few simple statements define the look and feel of a complete website regardless of the depth of the DOM.\nThink structured, even without a compiler Not being considered a programming language does not make structured and consistent thinking about markup and styling redundant. Quite the opposite. The cascade rewards clarity and penalises chaos. Understanding it — rather than working around it — is what separates frustrating CSS from CSS that actually scales.\n","permalink":"https://www.webstf.nl/posts/embrace-the-cascade/","tags":["css","front-end"],"title":"Embrace the Cascade"},{"categories":null,"content":"The ::column pseudo-element allows you to style individual columns in a CSS multi-column layout. Previously, there was no way to apply backgrounds, borders, or other styles to column boxes themselves — you could only style the multi-column container as a whole, or elements placed within it.\n1 2 3 4 5 6 7 8 9 .article { columns: 3; } .article::column { background: #f9f9f9; border-right: 1px solid #e0e0e0; padding: 1rem; } The set of properties available on ::column is limited to those that make sense for a column fragment box — primarily backgrounds, borders, padding, and similar box-decoration properties. Properties that affect layout or flow are not applicable.\nThis fills a gap in multi-column styling that previously required workarounds such as negative margins, wrapper elements, or JavaScript-measured positioning to achieve column-level visual treatment.\n","permalink":"https://www.webstf.nl/baseline/limited/#column-pseudo","tags":null,"title":"::column"},{"categories":null,"content":"The ::selection pseudo-element applies styles to the portion of a document that has been highlighted by the user — typically by clicking and dragging with a mouse or selecting text with a keyboard. It allows the default browser selection highlight to be replaced with custom colors.\n1 2 3 4 5 6 7 8 9 ::selection { background-color: oklch(70% 0.2 250); color: white; } .code-block::selection { background-color: oklch(80% 0.15 130); color: #1a1a1a; } Only a limited set of properties are supported on ::selection: color, background-color, text-decoration and its longhands, text-shadow, and stroke-color. The background shorthand is not supported — use background-color explicitly.\nCustom selection colors are a subtle but effective branding touch, particularly on editorial or portfolio sites. It is important to maintain sufficient contrast between the selection background and text color to keep selected text readable — the same accessibility considerations that apply to normal text apply here.\n::selection matches the active selection. Inactive selections (when the window loses focus) are not separately targetable in CSS and typically revert to a greyed-out system default.\n","permalink":"https://www.webstf.nl/baseline/limited/#selection","tags":null,"title":"::selection"},{"categories":null,"content":"The ::spelling-error and ::grammar-error pseudo-elements let you style the text that a browser or OS spell-checker has flagged, giving you control over how error highlights appear rather than leaving it entirely to the user agent\u0026rsquo;s default rendering (typically a red or green wavy underline).\n1 2 3 4 5 6 7 8 9 ::spelling-error { text-decoration: underline wavy #e53e3e; color: inherit; } ::grammar-error { text-decoration: underline wavy #d69e2e; color: inherit; } The properties you can apply to these pseudo-elements are limited to a subset of CSS — color, background, cursor, caret, outline, and text decoration properties — reflecting that the browser controls which ranges of text are marked, while CSS only controls their visual treatment.\nThis is most useful when your application has a specific design language and the browser\u0026rsquo;s default error styling clashes with it, or when you want to differentiate spelling and grammar errors more clearly than the defaults do.\n","permalink":"https://www.webstf.nl/baseline/limited/#spelling-grammar-error","tags":null,"title":"::spelling-error and ::grammar-error"},{"categories":null,"content":"The :has-slotted pseudo-class matches a \u0026lt;slot\u0026gt; element when it has been assigned slotted content — that is, when the slot has been filled by content from the light DOM. This allows web component authors to style slot elements differently depending on whether they contain content or are empty.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* Inside a web component\u0026#39;s shadow DOM stylesheet */ slot:not(:has-slotted) { display: none; /* Hide empty slots */ } .card__footer slot:has-slotted { padding-top: 1rem; border-top: 1px solid var(--border-color); } slot[name=\u0026#34;icon\u0026#34;]:has-slotted ~ .label { padding-left: 0.5rem; } Previously, detecting whether a slot was filled required JavaScript using slot.assignedNodes(), and conditionally applying classes or styles based on the result. :has-slotted brings this entirely into CSS, making it straightforward to conditionally show wrapper elements, adjust spacing, or shift layout based on whether optional slot content has been provided.\n","permalink":"https://www.webstf.nl/baseline/limited/#has-slotted","tags":null,"title":":has-slotted"},{"categories":null,"content":"The :host-context() pseudo-class, used inside a shadow DOM stylesheet, matches the shadow host element when any of its ancestors in the light DOM match the given selector. It allows a web component to adapt its styles based on the context in which it is placed, without requiring the consumer to pass that context in via attributes or custom properties.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* Inside a web component\u0026#39;s shadow DOM */ /* Style differently when inside a dark-themed container */ :host-context(.dark-theme) { --bg: #1a1a1a; --text: #f0f0f0; } /* Adjust layout when inside a sidebar */ :host-context(aside) { display: block; font-size: 0.875rem; } /* Respond to a data attribute on any ancestor */ :host-context([dir=\u0026#34;rtl\u0026#34;]) { text-align: right; } :host-context(selector) walks up the DOM tree from the shadow host through all its light DOM ancestors, matching if any of them satisfy the selector. This is particularly useful for theming — a component can automatically adopt a dark or high-contrast style when placed inside an appropriately marked container, without any direct communication between the container and the component.\nNote that :host-context() is considered at risk in the CSS specification and its future standardisation is uncertain. Browser support is reasonably good but it is not fully standardised, and CSS custom properties or explicit attribute-based theming (matched with :host([theme=\u0026quot;dark\u0026quot;])) are more robust alternatives for production component theming.\n","permalink":"https://www.webstf.nl/baseline/limited/#host-context","tags":null,"title":":host-context()"},{"categories":null,"content":"The :open pseudo-class matches interactive elements that are currently in an open or expanded state. It provides a unified selector for elements whose open state is toggled by the browser, covering \u0026lt;details\u0026gt;, \u0026lt;dialog\u0026gt;, \u0026lt;select\u0026gt;, \u0026lt;input\u0026gt; types with pickers, and elements with the popover attribute.\nPreviously, different elements required different selectors to target their open state: details[open] for details elements, no standard selector for open dialogs, and so on. :open unifies these under a single pseudo-class.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 details:open summary { font-weight: bold; } dialog:open { animation: fade-in 0.2s ease; } [popover]:open { opacity: 1; transform: translateY(0); } select:open { border-color: var(--color-focus); } Using :open makes styles more readable and consistent, and reduces the need to remember which attribute or state mechanism each element type uses for its open condition.\n","permalink":"https://www.webstf.nl/baseline/limited/#open-pseudo","tags":null,"title":":open"},{"categories":null,"content":"The :target-within pseudo-class matches an element when it or any of its descendants matches :target — that is, when the element contains the element whose id matches the current URL fragment. It is the target equivalent of :focus-within, propagating the targeted state up the DOM tree.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .section:target-within { background: oklch(97% 0.02 250); outline: 2px solid var(--color-primary); border-radius: 4px; } .card-list:target-within .card:not(:target) { opacity: 0.5; /* Dim non-targeted cards when one is targeted */ } nav:target-within { /* Highlight the nav when a nav item is targeted */ border-bottom: 2px solid var(--color-accent); } Where :target matches only the element with the matching id, :target-within allows ancestor elements to respond to a descendant being targeted. This makes it possible to highlight a containing section, card, or sidebar when a user navigates to an anchor link within it.\nA practical example is a FAQ list where each question has an anchor — :target-within on the \u0026lt;li\u0026gt; would highlight the entire question-and-answer pair when the question\u0026rsquo;s anchor is targeted, rather than just the heading element itself.\nAs of early 2026, :target-within was defined in the CSS Selectors Level 4 specification but had no browser support. It is a future-facing feature that mirrors the pattern established by :focus-within.\n","permalink":"https://www.webstf.nl/baseline/limited/#target-within","tags":null,"title":":target-within"},{"categories":null,"content":"The @function at-rule allows you to define reusable custom functions in CSS. A custom function takes parameters, performs calculations, and returns a value — similar to a function in a preprocessor like Sass, but built into the browser and reactive to the cascade.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @function --fluid-size(--min, --max, --min-width: 320px, --max-width: 1200px) { result: clamp( var(--min), calc(var(--min) + (var(--max) - var(--min)) * ((100vw - var(--min-width)) / (var(--max-width) - var(--min-width)))), var(--max) ); } h1 { font-size: --fluid-size(1.5rem, 3rem); } p { font-size: --fluid-size(1rem, 1.25rem); } Custom functions are defined with a name starting with --, accept typed or untyped parameters with optional defaults, and return a value via the result declaration. They participate in the cascade like any other CSS declaration and can reference other custom properties.\n@function reduces repetition in complex CSS calculations and makes intent clearer than deeply nested calc() expressions, without requiring a build step or preprocessor.\n","permalink":"https://www.webstf.nl/baseline/limited/#function","tags":null,"title":"@function"},{"categories":null,"content":"The alignment-baseline property specifies which baseline of an inline or SVG element is aligned to the dominant baseline of its parent. It determines the vertical alignment point used when placing the element within a line box, based on typographic baselines rather than the element\u0026rsquo;s edges.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tspan { alignment-baseline: alphabetic; } .math-symbol { alignment-baseline: mathematical; } .cjk-inline { alignment-baseline: ideographic; } text \u0026gt; tspan { alignment-baseline: middle; } The available values correspond to typographic baseline positions: alphabetic (the Latin text baseline), ideographic (the baseline used for CJK ideographs), mathematical (the baseline for mathematical notation), central, middle, text-before-edge, text-after-edge, before-edge, and after-edge.\nalignment-baseline is primarily a property for SVG text layout, where precise control over how inline elements and text spans align relative to the parent text element is needed. In HTML contexts, vertical-align handles most analogous use cases, though alignment-baseline maps more directly to the underlying font metric system. It works in conjunction with dominant-baseline, which establishes the baseline of the parent against which child elements are aligned.\n","permalink":"https://www.webstf.nl/baseline/limited/#alignment-baseline","tags":null,"title":"alignment-baseline"},{"categories":null,"content":"Alternative style sheets allow a page to offer multiple named stylesheets that the user can switch between. They are defined using \u0026lt;link\u0026gt; elements in HTML with a rel=\u0026quot;alternate stylesheet\u0026quot; attribute and a title attribute identifying the style set. The default stylesheet uses rel=\u0026quot;stylesheet\u0026quot; with a title.\n1 2 3 4 5 6 7 8 9 \u0026lt;!-- Persistent stylesheet (always active) --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;base.css\u0026#34;\u0026gt; \u0026lt;!-- Default preferred stylesheet --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; title=\u0026#34;Default\u0026#34; href=\u0026#34;default.css\u0026#34;\u0026gt; \u0026lt;!-- Alternative stylesheets --\u0026gt; \u0026lt;link rel=\u0026#34;alternate stylesheet\u0026#34; title=\u0026#34;High contrast\u0026#34; href=\u0026#34;high-contrast.css\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;alternate stylesheet\u0026#34; title=\u0026#34;Large text\u0026#34; href=\u0026#34;large-text.css\u0026#34;\u0026gt; A stylesheet with a title but without alternate is the preferred stylesheet — active by default. Alternative stylesheets are inactive until selected. Persistent stylesheets (no title) are always applied regardless of which alternative is active.\nSome browsers — historically Firefox — exposed a built-in UI for switching between alternative stylesheets via the View menu. This native browser support is inconsistent and largely absent from modern browsers, so switching is typically implemented with JavaScript that sets sheet.disabled on the appropriate \u0026lt;link\u0026gt; elements.\nAlternative style sheets represent an early web platform feature for user-selectable themes. The same goals are more commonly achieved today with CSS custom properties, prefers-color-scheme media queries, and JavaScript-driven class or attribute toggling on the root element.\n","permalink":"https://www.webstf.nl/baseline/limited/#alternative-style-sheets","tags":null,"title":"Alternative style sheets"},{"categories":null,"content":"The at-rule() function is used inside @supports conditions to test whether the browser supports a specific CSS at-rule. This extends @supports beyond property-value pairs to enable feature detection of at-rules like @layer, @property, @font-face descriptors, and others.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @supports at-rule(@property) { /* Only applies if @property is supported */ @property --my-color { syntax: \u0026#39;\u0026lt;color\u0026gt;\u0026#39;; inherits: false; initial-value: blue; } .element { --my-color: red; color: var(--my-color); } } @supports at-rule(@layer) { /* Styles that depend on cascade layers being available */ @layer base { body { margin: 0; } } } Without at-rule(), there was no way to query whether a particular at-rule was understood by the browser in CSS — you could only test properties and values, or selectors with the selector() function. at-rule() fills that gap for at-rule feature detection.\nThis is particularly useful for @property, which enables typed and animatable custom properties — a significantly different capability from basic custom properties — and where a fallback to untyped custom properties may be appropriate for non-supporting browsers.\nAs of early 2026, at-rule() support in @supports was still limited across browsers and should be used with awareness of its availability.\n","permalink":"https://www.webstf.nl/baseline/limited/#supports-at-rule","tags":null,"title":"at-rule()"},{"categories":null,"content":"The attr() function retrieves the value of an HTML attribute from the element and makes it available as a string in CSS. In its baseline form — widely supported across all browsers — it works only inside the content property of ::before and ::after pseudo-elements, where it inserts the attribute value as generated text content.\n1 2 3 4 5 6 7 8 9 10 11 a[href]::after { content: \u0026#39; (\u0026#39; attr(href) \u0026#39;)\u0026#39;; } abbr[title]::after { content: \u0026#39; [\u0026#39; attr(title) \u0026#39;]\u0026#39;; } [data-label]::before { content: attr(data-label); } This makes it possible to surface HTML attribute values visually in the rendered page without duplicating them in the markup or reaching for JavaScript. A common use case is print stylesheets that append URLs after links, since the href is not otherwise visible in print.\nA more advanced form of attr() — which allows the function to be used in any CSS property and supports typed values like lengths, colors, and numbers — is defined in the specification but has limited browser support. The content-only form described here is the reliably available baseline that works in all modern browsers.\n","permalink":"https://www.webstf.nl/baseline/limited/#attr-contents","tags":null,"title":"attr() (content only)"},{"categories":null,"content":"The border-area value for background-clip restricts the background to paint only within the border itself, excluding the padding and content areas. This is distinct from the existing border-box value, which paints the background across the entire border box (content + padding + border), with the border drawn on top.\nborder-area makes it straightforward to apply a gradient or image directly to the border region — an effect that was previously achievable only through layered backgrounds, pseudo-elements, or border-image.\n1 2 3 4 5 .fancy-border { border: 8px solid transparent; background-clip: border-area; background-image: linear-gradient(135deg, #f093fb, #f5576c); } A common pattern pairs border-area with a separate background for the content area, using multiple background-clip layers:\n1 2 3 4 5 6 .gradient-border { border: 4px solid transparent; background: linear-gradient(white, white) padding-box, linear-gradient(to right, #6ee7f7, #a78bfa) border-area; } This renders a white content area with a gradient border, cleanly and without pseudo-elements.\n","permalink":"https://www.webstf.nl/baseline/limited/#background-clip-border-area","tags":null,"title":"background-clip: border-area"},{"categories":null,"content":"background-clip: text clips an element\u0026rsquo;s background to the shape of its text characters, creating gradient or image-filled typography entirely in CSS.\n1 2 3 4 5 6 h1 { background: linear-gradient(135deg, #f093fb, #f5576c); -webkit-background-clip: text; background-clip: text; color: transparent; } color: transparent reveals the clipped background through the text.\nBackground-clip example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /* Animated Gradient */ @keyframes shift { from { background-position: 0% 50%; } to { background-position: 100% 50%; } } .animated-heading { background: linear-gradient(90deg, #4facfe, #00f2fe, #4facfe); background-size: 200% auto; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shift 3s linear infinite; } /* With images */ .textured { background-image: url(\u0026#34;texture.jpg\u0026#34;); background-size: cover; -webkit-background-clip: text; background-clip: text; color: transparent; } Always include the prefix, -webkit-background-clip: text is still required alongside the standard property for complete browser coverage.\n","permalink":"https://www.webstf.nl/baseline/limited/#background-clip-text","tags":null,"title":"background-clip: text"},{"categories":null,"content":"The baseline-shift property moves an inline element up or down relative to the dominant baseline of its parent. It is the CSS equivalent of the SVG baseline-shift presentation attribute, and is primarily used in SVG text to produce superscript and subscript effects without changing font size.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tspan.superscript { baseline-shift: super; } tspan.subscript { baseline-shift: sub; } tspan.raised { baseline-shift: 0.4em; } tspan.lowered { baseline-shift: -0.3em; } The super and sub keywords shift the element to a typical superscript or subscript position relative to the parent\u0026rsquo;s baseline. Length and percentage values provide precise control over the shift amount — positive values move the element up, negative values move it down.\nIn HTML, vertical-align with super and sub values, or with explicit length offsets, serves the same purpose for inline elements. baseline-shift is specifically relevant when authoring SVG text or working in contexts where SVG\u0026rsquo;s typographic model applies. The two properties address the same visual intent through different specifications: vertical-align for HTML inline layout, baseline-shift for SVG text layout.\n","permalink":"https://www.webstf.nl/baseline/limited/#baseline-shift","tags":null,"title":"baseline-shift"},{"categories":null,"content":"calc-size() extends the capabilities of calc() to work with intrinsic size keywords like auto, min-content, max-content, and fit-content — values that calc() cannot handle because they are not fixed lengths.\nThis opens up arithmetic operations involving intrinsic sizes, which was previously impossible in CSS without JavaScript measurement. It also plays a key role in enabling transitions and animations to and from intrinsic sizes.\n1 2 3 4 5 6 7 8 9 10 11 12 .panel { width: calc-size(max-content, size + 2rem); } .expandable { height: 0; transition: height 0.3s ease; \u0026amp;.open { height: calc-size(auto, size); } } In the transition example, calc-size(auto, size) resolves to the element\u0026rsquo;s natural auto height, allowing a smooth animation that was not achievable before without JavaScript to measure and set explicit pixel values. calc-size() works alongside interpolate-size to make this kind of intrinsic-size animation possible declaratively.\n","permalink":"https://www.webstf.nl/baseline/limited/#calc-size","tags":null,"title":"calc-size()"},{"categories":null,"content":"The s flag in CSS attribute selectors enforces case-sensitive matching of attribute values. In HTML, attribute values are matched case-insensitively by default for certain attributes, but the s flag overrides this to require an exact case match.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* Only matches type=\u0026#34;text\u0026#34;, not type=\u0026#34;TEXT\u0026#34; or type=\u0026#34;Text\u0026#34; */ input[type=\u0026#34;text\u0026#34; s] { border: 1px solid #ccc; } /* Only matches the exact data attribute value */ [data-status=\u0026#34;Active\u0026#34; s] { color: green; } /* Only matches href ending in exactly \u0026#34;.PDF\u0026#34; */ a[href$=\u0026#34;.PDF\u0026#34; s] { /* Would not match .pdf or .Pdf */ } The s flag is placed inside the brackets after the value, separated by whitespace — the same position as the i (case-insensitive) flag. The two flags are mutually exclusive.\nIn HTML documents, attribute value matching is case-insensitive for certain well-known attributes by default — type, rel, for, and others. The s flag forces case-sensitive matching even for these, while for attributes that are already case-sensitive by default (such as data-* attributes and most others in HTML), the s flag makes the case sensitivity explicit without changing behaviour.\nCase-sensitive matching is most relevant in XML and SVG contexts where attribute values are inherently case-sensitive, or in HTML where you specifically need to distinguish between data-status=\u0026quot;active\u0026quot; and data-status=\u0026quot;Active\u0026quot; as distinct states.\n","permalink":"https://www.webstf.nl/baseline/limited/#case-sensitive-attributes","tags":null,"title":"Case-sensitive attribute selector"},{"categories":null,"content":"The color-contrast() function selects the colour from a list that has the highest contrast ratio against a specified background colour. It automates the common task of choosing between a dark or light foreground colour to ensure readable text over a dynamic background.\n1 2 3 4 5 .badge { --bg: var(--brand-color); background: var(--bg); color: color-contrast(var(--bg) vs white, black); } The syntax specifies a base colour, the keyword vs, and then a comma-separated list of candidate colours to compare against it. The function returns whichever candidate achieves the highest contrast ratio with the base.\n1 2 3 4 5 6 7 8 9 .button { background: var(--accent); color: color-contrast( var(--accent) vs white, #1a1a1a, var(--color-text-muted) ); } An optional target contrast level can be added using to, which causes the function to return the first candidate that meets or exceeds the specified level rather than simply the highest contrast option:\n1 2 3 .label { color: color-contrast(var(--bg) vs white, black to AA); } The AA and AAA keywords correspond to the WCAG 2.1 contrast ratio thresholds of 4.5:1 and 7:1 respectively. A specific ratio can also be provided as a number.\ncolor-contrast() is closely related to contrast-color(), which is a simpler function returning whichever of black or white contrasts best with a given colour. color-contrast() is the more powerful version, accepting an arbitrary list of candidates and optional WCAG targets, though browser support is still limited and it is currently behind flags in most browsers.\n","permalink":"https://www.webstf.nl/baseline/limited/#color-contrast","tags":null,"title":"color-contrast()"},{"categories":null,"content":"Scroll-state container queries extend the @container query syntax to allow styling based on the scroll state of a scroll container. Rather than querying size or style, these queries respond to whether a container is currently scrolled, stuck (as a sticky element), or snapped (as a scroll snap target).\nA container must be declared as a scroll-state container before it can be queried:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .scroller { container-type: scroll-state; overflow-y: auto; } @container scroll-state(scrollable: top) { .scroll-hint { opacity: 0; } } @container scroll-state(scrollable: bottom) { .load-more { display: block; } } Sticky element state can also be queried, removing the need for IntersectionObserver hacks to detect when a sticky header is actively stuck:\n1 2 3 4 5 6 7 8 9 10 11 .sticky-header { container-type: scroll-state; position: sticky; top: 0; } @container scroll-state(stuck: top) { .sticky-header { box-shadow: 0 2px 8px rgb(0 0 0 / 0.15); } } Scroll-state queries bring a class of UI behaviours — scroll-aware components, sticky state styling, snap position detection — into CSS without JavaScript event listeners.\n","permalink":"https://www.webstf.nl/baseline/limited/#container-scroll-state-queries","tags":null,"title":"Container scroll-state queries"},{"categories":null,"content":"Container style queries let you apply styles based on the computed value of a CSS custom property on a container element — not just its size. This enables state-driven styling through CSS alone.\n1 2 3 4 5 6 7 8 9 10 11 .card-wrapper { container-name: card; --variant: featured; } @container card style(--variant: featured) { .card { border: 2px solid gold; background: #fffbeb; } } When the container has --variant: featured, the card gets special styling.\nStyle queries can be used for theming\n1 2 3 4 5 6 7 8 .theme-wrapper { container-name: theme; --color-scheme: dark; } @container theme style(--color-scheme: dark) { .component { background: #1a1a2e; color: #e0e0e0; } } Quite powerful, because previously, changing styles based on a parent\u0026rsquo;s state required JavaScript adding classes. Style queries allow this with pure CSS custom properties.\nContainer style queries can be combined with size queries\n1 2 3 @container sidebar (width \u0026lt; 200px) and style(--compact: true) { .nav-item { padding: 0.25rem; } } ","permalink":"https://www.webstf.nl/baseline/limited/#container-style-queries","tags":null,"title":"Container style queries"},{"categories":null,"content":"The contrast-color() function returns the color from a provided list that has the highest contrast against a given background color. Its initial, simplified form takes a single background color and returns whichever of white or black contrasts best with it.\n1 2 3 4 5 .badge { --bg: var(--brand-color); background: var(--bg); color: contrast-color(var(--bg)); } This eliminates a very common problem in dynamic theming: ensuring text remains readable over a background whose color is set at runtime via a custom property or user preference. Previously this required JavaScript to calculate luminance and decide between dark and light text. contrast-color() does this in CSS, reacting automatically to changes in the background value.\nThe longer-term specification extends the function to accept an arbitrary list of candidate colors, returning whichever achieves the best contrast ratio, along with optional target contrast level constraints. The simpler contrast-color(color) form returning black or white is the initial implementation available in browsers.\n","permalink":"https://www.webstf.nl/baseline/limited/#contrast-color","tags":null,"title":"contrast-color()"},{"categories":null,"content":"The DOM Level 2 CSS specification defined the original programmatic interface for accessing and manipulating CSS stylesheets — the interfaces that underpin document.styleSheets, CSSStyleSheet, CSSRuleList, and CSSStyleRule. While still functional and widely used, parts of this API are considered legacy and some patterns it enabled are discouraged in modern development.\nThe most common discouraged pattern is directly modifying stylesheet rules via CSSStyleSheet.insertRule() and deleteRule() for general styling tasks, or accessing element.style for computed values:\n1 2 3 4 5 6 7 8 9 10 11 12 // Discouraged: accessing element.style for computed values // element.style only reflects inline styles, not computed styles const color = element.style.color; // May be empty even if color is set by stylesheet // Correct: use getComputedStyle for resolved values const color = window.getComputedStyle(element).color; // Discouraged for dynamic theming: manipulating stylesheet rules directly document.styleSheets[0].insertRule(\u0026#39;.theme { color: red }\u0026#39;, 0); // Preferred: use CSS custom properties instead document.documentElement.style.setProperty(\u0026#39;--theme-color\u0026#39;, \u0026#39;red\u0026#39;); The DOM Level 2 API also uses string-based property access (cssRule.style.setProperty('color', 'red')) which lacks type safety. The CSS Typed Object Model (Houdini) provides a more modern, type-safe alternative via element.attributeStyleMap and element.computedStyleMap().\nFor reading computed styles, getComputedStyle() remains the standard approach. For dynamic styling, CSS custom properties manipulated via style.setProperty() are strongly preferred over direct stylesheet manipulation.\n","permalink":"https://www.webstf.nl/baseline/limited/#css-object-model-discouraged","tags":null,"title":"CSS object model (DOM level 2)"},{"categories":null,"content":"The CSS Typed Object Model (Typed OM) is a JavaScript API that exposes CSS property values as typed objects rather than plain strings, making programmatic CSS manipulation faster, safer, and easier.\n1 2 3 4 5 6 7 8 9 10 11 12 /* reading values */ const el = document.querySelector(\u0026#39;.box\u0026#39;); const styles = el.computedStyleMap(); const width = styles.get(\u0026#39;width\u0026#39;); console.log(width.value); // 200 console.log(width.unit); // \u0026#34;px\u0026#34; /* writing values */ el.attributeStyleMap.set(\u0026#39;width\u0026#39;, CSS.px(300)); el.attributeStyleMap.set(\u0026#39;opacity\u0026#39;, CSS.number(0.5)); el.attributeStyleMap.set(\u0026#39;transform\u0026#39;, new CSSTranslate(CSS.px(10), CSS.px(20))); CSS Factory Functions\n1 2 3 4 CSS.px(10) // CSSUnitValue { value: 10, unit: \u0026#39;px\u0026#39; } CSS.percent(50) // CSSUnitValue { value: 50, unit: \u0026#39;percent\u0026#39; } CSS.deg(45) // CSSUnitValue { value: 45, unit: \u0026#39;deg\u0026#39; } CSS.number(1.5) // CSSUnitValue { value: 1.5, unit: \u0026#39;number\u0026#39; } Why this is better than getComputedStyle\nNo string parsing — values come as typed objects with .value and .unit Math is straightforward: width.value + 10 instead of parseFloat(width) + 10 Errors are caught early with meaningful messages ","permalink":"https://www.webstf.nl/baseline/limited/#css-typed-om","tags":null,"title":"CSS typed object model"},{"categories":null,"content":"The cursor property sets the type of cursor shown when the mouse pointer is over an element. It communicates affordance — what the user can do with the element — and is an important part of interactive UI design.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .button { cursor: pointer; /* Hand cursor, signals clickability */ } .disabled { cursor: not-allowed; } .draggable { cursor: grab; } .draggable:active { cursor: grabbing; } .resizable { cursor: ew-resize; /* East-west resize arrow */ } .text-area { cursor: text; } The most commonly used keyword values include default (the standard arrow), pointer (pointing hand for links and buttons), text (I-beam for text), move (four-directional arrow), grab and grabbing (for draggable elements), not-allowed (blocked action), wait (loading), crosshair, and a full set of directional resize cursors (n-resize, ew-resize, nwse-resize, etc.).\nCustom cursors can be specified using url(), with fallback keywords:\n1 2 3 .custom { cursor: url(\u0026#39;cursor.png\u0026#39;) 8 8, pointer; } The two numbers after the URL are the cursor\u0026rsquo;s hotspot coordinates — the pixel offset of the active click point from the top-left of the image. A fallback keyword is required after any url() value.\ncursor: none hides the cursor entirely, which is occasionally used in games or immersive experiences that render their own custom cursor with JavaScript.\n","permalink":"https://www.webstf.nl/baseline/limited/#cursor","tags":null,"title":"Cursor styles"},{"categories":null,"content":"Device media queries target characteristics of the output device or environment beyond viewport dimensions — things like screen resolution, display color capability, pointer precision, and user preferences. They complement width-based responsive breakpoints by allowing styles to adapt to the nature of the device itself rather than just its size.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /* High-resolution / retina displays */ @media (resolution \u0026gt;= 2dppx) { .logo { background-image: url(\u0026#39;logo@2x.png\u0026#39;); } } /* Devices with a precise pointer (mouse) */ @media (pointer: fine) { .button { padding: 0.5rem 1rem; } } /* Devices with a coarse pointer (touch) */ @media (pointer: coarse) { .button { padding: 0.75rem 1.25rem; /* Larger touch target */ } } /* Devices that support hover */ @media (hover: hover) { .card:hover { transform: translateY(-2px); } } /* color display capability */ @media (color) { /* Device supports color */ } @media (monochrome) { /* Black and white display */ } The prefers-* media features query user preferences set at the OS level:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } @media (prefers-contrast: more) { :root { --color-text: black; --color-bg: white; } } @media (prefers-reduced-data: reduce) { .hero-video { display: none; } } Device queries are particularly important for accessibility — prefers-reduced-motion should be respected in any site that uses animation, and prefers-contrast allows high-contrast mode users to receive appropriately adjusted styles.\n","permalink":"https://www.webstf.nl/baseline/limited/#device-queries","tags":null,"title":"Device media queries"},{"categories":null,"content":"display is now animatable as a discrete property, meaning it can participate in CSS transitions and keyframe animations when transition-behavior: allow-discrete is set. Previously, display toggled instantly between values with no interpolation, making it impossible to animate elements in or out while simultaneously hiding them from the layout.\nWith allow-discrete, the browser handles the timing of the discrete switch intelligently: when transitioning to display: none, the switch happens at the end of the transition (after other properties like opacity have finished animating); when transitioning from display: none, the switch happens at the start (so the element is visible while other properties animate in).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .toast { display: block; opacity: 1; transition: opacity 0.3s ease, display 0.3s ease; transition-behavior: allow-discrete; @starting-style { opacity: 0; } } .toast.hidden { display: none; opacity: 0; } This pattern, combined with @starting-style for the enter state, provides complete enter and exit animations on display-toggled elements using only CSS. The same approach works with content-visibility and overlay for popover and dialog animations.\n","permalink":"https://www.webstf.nl/baseline/limited/#display-animation","tags":null,"title":"display animation"},{"categories":null,"content":"The display-mode media feature detects how a web application is currently being displayed — as a regular browser tab, a standalone installed PWA, a fullscreen app, or in a minimal browser UI. It allows CSS to adapt the interface based on the launch context.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 /* Default browser tab */ @media (display-mode: browser) { .install-prompt { display: block; } } /* Installed as a standalone PWA */ @media (display-mode: standalone) { .install-prompt { display: none; /* Already installed, hide the prompt */ } .nav-bar { padding-top: env(safe-area-inset-top); /* Account for notch */ } } /* Fullscreen mode */ @media (display-mode: fullscreen) { .exit-fullscreen-button { display: block; } } /* Minimal browser UI */ @media (display-mode: minimal-ui) { .custom-back-button { display: flex; } } The values correspond to the display field in a web app manifest: browser (normal tab), minimal-ui (browser with minimal UI controls), standalone (app-like, no browser chrome), and fullscreen (no browser UI at all).\ndisplay-mode is particularly useful in progressive web apps to hide browser-specific UI elements (like install prompts or navigation chrome) when the app is running in standalone or fullscreen mode, and to adjust layout for the absence of browser navigation controls.\n","permalink":"https://www.webstf.nl/baseline/limited/#display-mode","tags":null,"title":"display-mode media query"},{"categories":null,"content":"display: contents causes an element to generate no box of its own. The element itself is not rendered, but its children are treated as if they were direct children of the element\u0026rsquo;s parent in the layout. The element effectively disappears from the box tree while remaining in the DOM.\n1 2 3 4 5 .wrapper { display: contents; /* This element generates no box — its children participate directly in the parent\u0026#39;s layout context */ } A common use case is making a semantically useful wrapper element invisible to the layout system. For example, a \u0026lt;fieldset\u0026gt; or \u0026lt;ul\u0026gt; might be needed for semantic or accessibility reasons, but its default box model interferes with a grid or flex layout on the parent. Setting display: contents removes the wrapper from the layout without removing it from the document.\n1 2 3 4 5 6 7 8 .grid { display: grid; grid-template-columns: repeat(3, 1fr); } .grid-section { display: contents; /* Children of this element become direct grid items */ } 1 2 3 4 5 6 7 \u0026lt;div class=\u0026#34;grid\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;grid-section\u0026#34;\u0026gt; \u0026lt;div\u0026gt;Item 1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;Item 2\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;Item 3\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; There is an important accessibility caveat: some browsers incorrectly remove elements with display: contents from the accessibility tree entirely, meaning screen readers may not announce the element or its semantic role. This makes it unsuitable for elements whose semantics matter — such as \u0026lt;button\u0026gt;, \u0026lt;a\u0026gt;, \u0026lt;fieldset\u0026gt;, or heading elements — until browser support improves. For purely presentational wrapper \u0026lt;div\u0026gt; elements it is generally safe.\n","permalink":"https://www.webstf.nl/baseline/limited/#display-contents","tags":null,"title":"display: contents"},{"categories":null,"content":"The field-sizing property controls whether form fields size themselves based on their content or maintain a fixed size. The default behaviour for text inputs and textareas is field-sizing: fixed — they have a defined size and do not grow or shrink as the user types. Setting field-sizing: content changes this so the field adjusts its size to fit its current value.\n1 2 3 4 5 6 7 8 9 10 textarea { field-sizing: content; min-height: 3lh; max-height: 20lh; } input[type=\u0026#34;text\u0026#34;] { field-sizing: content; min-width: 10ch; } With field-sizing: content, a textarea grows as the user types more content, up to any max-height you set, and a text input expands horizontally to fit its value. Previously, this behaviour required JavaScript event listeners on the input to manually adjust dimensions.\nCombining field-sizing: content with min-* and max-* properties gives you full control over the range of sizes the field can take, keeping the experience predictable while still being responsive to the user\u0026rsquo;s content.\n","permalink":"https://www.webstf.nl/baseline/limited/#field-sizing","tags":null,"title":"field-sizing"},{"categories":null,"content":"The CSS filter property applies graphical effects — blur, brightness, contrast, color shifts, shadows, and more — to an element and all of its contents. Filter functions can be chained, and each is applied in sequence to the output of the previous.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .blurred { filter: blur(4px); } .dark-overlay { filter: brightness(0.6); } .grayscale-image { filter: grayscale(100%); } .vintage-photo { filter: sepia(60%) contrast(1.2) saturate(0.8); } .drop-shadow { filter: drop-shadow(4px 4px 8px rgb(0 0 0 / 0.3)); } The available filter functions are blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), and sepia().\nfilter: drop-shadow() is distinct from box-shadow — it follows the actual shape of the element including transparent areas, making it ideal for PNG images and SVG elements where a shadow should trace the content\u0026rsquo;s outline rather than its rectangular box.\nApplying filter creates a new stacking context and, for most filter types, triggers GPU compositing. This is both a performance benefit for animated filters and a potential source of z-index surprises. filter: none removes all filters.\nbackdrop-filter applies the same filter functions to the area behind an element rather than the element itself, enabling the frosted glass effect.\n","permalink":"https://www.webstf.nl/baseline/limited/#filter-function","tags":null,"title":"filter()"},{"categories":null,"content":"The fit-content() function is used as a track sizing function in CSS Grid, setting a track to grow to fit its content up to a specified maximum size. It is equivalent to min(max-content, max(min-content, argument)) — the track sizes to its content naturally, but is clamped at the provided maximum.\n1 2 3 .grid { grid-template-columns: fit-content(200px) 1fr fit-content(300px); } In this example, the first and third columns size to their content but will not grow beyond 200px and 300px respectively. The middle column takes up all remaining space with 1fr.\n1 2 3 4 5 6 7 8 9 10 .sidebar-layout { display: grid; grid-template-columns: fit-content(250px) 1fr; } .label-grid { display: grid; grid-template-columns: fit-content(120px) auto; gap: 0.5rem 1rem; } fit-content() is distinct from the fit-content keyword (without parentheses), which is used as a width or height value on regular elements. The function form is specifically for grid track sizing and requires an argument defining the maximum size the track may reach.\nThis makes it ideal for sidebar columns, label columns in form layouts, and any grid track that should shrink-wrap its content but be bounded to prevent it from becoming too wide when content is long.\n","permalink":"https://www.webstf.nl/baseline/limited/#fit-content-function","tags":null,"title":"fit-content()"},{"categories":null,"content":"The font-language-override property controls which language-specific glyph variants a font uses, independently of the document\u0026rsquo;s lang attribute. Many OpenType fonts include alternate glyphs optimised for particular languages — the same Unicode character may be rendered differently in Serbian versus Russian, or in Traditional versus Simplified Chinese.\nNormally, the browser selects these variants based on the element\u0026rsquo;s language as set by lang. font-language-override lets you specify a different OpenType language system tag directly, overriding that automatic selection.\n1 2 3 4 5 6 7 8 .serbian-text { /* Use Serbian-specific glyph variants regardless of document lang */ font-language-override: \u0026#39;SRB\u0026#39;; } .romanian-text { font-language-override: \u0026#39;ROM\u0026#39;; } The value is an OpenType language system tag — a four-character string defined by the OpenType specification. The default value normal defers to the language inferred from the lang attribute.\nThis property is most useful when document-level language tagging does not match the language of a specific block of text, or when you need to mix glyph sets from different language variants within a single document.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-language-override","tags":null,"title":"font-language-override"},{"categories":null,"content":"The font-palette property selects a color palette from a color font\u0026rsquo;s built-in palette definitions. color fonts (in formats like COLRv1) contain multiple named or indexed palettes that define how their built-in colors are applied. font-palette lets you switch between them or define custom overrides using @font-palette-values.\nWhat makes this particularly powerful is that font-palette is animatable. You can transition smoothly between two palettes — or between a palette and a custom color override — creating animated color effects in multi-color fonts without any JavaScript or SVG tricks.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @font-palette-values --pale { font-family: \u0026#39;MyColorFont\u0026#39;; override-colors: 0 #e8d5c0, 1 #c4a882; } @font-palette-values --vivid { font-family: \u0026#39;MyColorFont\u0026#39;; override-colors: 0 #ff4500, 1 #ff8c00; } .logo { font-palette: --pale; transition: font-palette 0.4s ease; } .logo:hover { font-palette: --vivid; } The browser interpolates between the individual color values in the palettes, producing a smooth cross-fade of the font\u0026rsquo;s colors on hover, focus, or any other state change.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-palette-animation","tags":null,"title":"font-palette-animation"},{"categories":null,"content":"font-synthesis-position controls whether the browser synthesises superscript and subscript positioning when the font does not include dedicated superior or inferior glyphs.\nWhen using font-variant-position: super or sub, browsers ideally use specially designed smaller glyphs positioned at the correct height. Without them, the browser scales down regular glyphs and repositions them — faux positioning.\n1 2 3 :root { font-synthesis-position: none; } With synthesis disabled, font-variant-position: super or sub has no effect if the font lacks true positional glyphs — preventing poorly scaled substitutes.\n1 font-synthesis-position: auto; This is a longhand of the font-synthesis shorthand:\n1 font-synthesis: none; /* disables weight, style, small-caps, AND position */ font-variant-position uses designed glyphs at appropriate sizes. vertical-align: super/sub simply shifts regular-sized text, which looks too large for footnote markers or chemical formulas.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-synthesis-position","tags":null,"title":"font-synthesis-position"},{"categories":null,"content":"The font-variant-emoji property controls whether emoji characters are rendered in their colorful emoji presentation or as plain text symbols. Unicode defines variation selectors that influence this (VS15 for text, VS16 for emoji), but font-variant-emoji gives you CSS-level control over the default presentation when no variation selector is present in the markup.\n1 2 3 4 5 6 7 8 9 10 11 .monochrome-icons { font-variant-emoji: text; } .colorful-labels { font-variant-emoji: emoji; } .system-default { font-variant-emoji: normal; } The text value renders the character as a monochrome text glyph, while emoji requests the full-color emoji rendering. unicode defers to the character\u0026rsquo;s default presentation as defined by Unicode, and normal leaves the decision to the browser and font.\nThis is useful when you want to use emoji characters as icon-like elements that blend with surrounding text styling, or conversely when you want to ensure colorful rendering in contexts where the text presentation might otherwise be selected.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-variant-emoji","tags":null,"title":"font-variant-emoji"},{"categories":null,"content":"The font-variant-position property renders text as superscript or subscript using dedicated glyphs from the font, rather than the scaled and repositioned approach used by the HTML \u0026lt;sup\u0026gt; and \u0026lt;sub\u0026gt; elements. When a font includes purpose-designed superior and inferior figures and letters, they are optically better than synthesised alternatives.\n1 2 3 4 5 6 7 8 9 10 11 .footnote-marker { font-variant-position: super; } .chemical-formula sub { font-variant-position: sub; } .reset { font-variant-position: normal; } The super value uses OpenType superior glyphs (sups feature) — smaller letterforms drawn specifically for superscript use, with strokes and proportions optimised at their intended small size. The sub value uses inferior glyphs (subs feature) for subscripts. These are distinct from scaled-down regular glyphs, which appear thin and spindly at small sizes.\nIf the font does not include superior or inferior glyphs, the browser may fall back to synthesised positioning or simply render the text normally. font-synthesis: position controls whether synthesis is permitted when the actual glyphs are unavailable.\nThe difference from vertical-align: super and vertical-align: sub is that font-variant-position uses glyphs designed for the purpose, while vertical-align repositions regular-sized or font-size-reduced characters — which can disrupt line spacing when the repositioned characters extend beyond the line box.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-variant-position","tags":null,"title":"font-variant-position"},{"categories":null,"content":"The font-width property selects a font face based on its width — how condensed or expanded the letterforms are. It is the standardised longhand for what font-stretch has represented, providing a cleaner name that better reflects what is being controlled.\nFor variable fonts that include a width axis (wdth), font-width can be set to any percentage value within the font\u0026rsquo;s supported range. For non-variable fonts, the browser selects the closest available face.\n1 2 3 4 5 6 7 8 9 10 11 .condensed-label { font-width: 75%; /* Condensed */ } .expanded-headline { font-width: 125%; /* Expanded */ } .normal-body { font-width: normal; /* 100% */ } Named keywords are also accepted: ultra-condensed (50%), extra-condensed (62.5%), condensed (75%), semi-condensed (87.5%), normal (100%), semi-expanded (112.5%), expanded (125%), extra-expanded (150%), and ultra-expanded (200%).\nfont-width is particularly useful with variable fonts in editorial or display contexts where fine control over the horizontal density of text contributes to the typographic character of the design.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-width","tags":null,"title":"font-width"},{"categories":null,"content":"Gap decorations are a set of CSS properties that allow visual styling to be applied directly to the gaps between items in flex, grid, and multi-column layouts. Rather than using borders, pseudo-elements, or background tricks to simulate dividers, gap decoration properties paint directly into the gap area.\n1 2 3 4 5 6 7 8 9 10 11 12 .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; gap-decoration: 1px solid #e0e0e0; } .flex-row { display: flex; gap: 1.5rem; column-gap-decoration: 1px dashed #ccc; } The gap decoration properties follow a similar model to border — they accept a width, style, and colour. The shorthand gap-decoration sets the decoration for all gaps, while row-gap-decoration and column-gap-decoration target horizontal and vertical gaps independently.\n1 2 3 4 5 6 7 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1px; gap-decoration: 1px solid var(--color-border); gap-decoration-break: intersecting; /* Controls how decorations meet at intersections */ } Gap decorations resolve a long-standing CSS pain point — drawing dividers between grid or flex items without resorting to border on each item (which creates double borders at shared edges) or complex :not(:last-child) selector hacks. This feature is part of the CSS Box Alignment specification and was still gaining browser support as of early 2026.\n","permalink":"https://www.webstf.nl/baseline/limited/#gap-decorations","tags":null,"title":"Gap decorations"},{"categories":null,"content":"The glyph-orientation-vertical property was an SVG presentation attribute and early CSS property that controlled the orientation of glyphs when text was set in a vertical writing mode. It specified the angle at which characters were rotated within a vertical text run.\n1 2 3 4 5 6 7 8 9 /* Legacy SVG usage */ text { writing-mode: tb; glyph-orientation-vertical: 0; /* Upright */ } text.rotated { glyph-orientation-vertical: 90; /* Rotated 90 degrees */ } The property accepted angle values in degrees (without a unit), where 0 rendered glyphs upright and 90 rotated them sideways — the typical orientation for Latin text set within a vertical CJK text run.\nglyph-orientation-vertical is now obsolete and has been replaced by the text-orientation property, which handles glyph orientation in vertical text more cleanly and as part of the standardised CSS Writing Modes specification.\n1 2 3 4 5 6 7 8 9 10 /* Modern equivalent */ .vertical-text { writing-mode: vertical-rl; text-orientation: mixed; /* CJK upright, Latin rotated */ } .all-upright { writing-mode: vertical-rl; text-orientation: upright; /* All characters upright */ } text-orientation: mixed is the standard default for vertical text — it keeps CJK characters upright while rotating Latin and other scripts 90 degrees. For new projects, text-orientation should be used instead of glyph-orientation-vertical.\n","permalink":"https://www.webstf.nl/baseline/limited/#glyph-orientation-vertical","tags":null,"title":"glyph-orientation-vertical"},{"categories":null,"content":"CSS does not have dedicated heading pseudo-classes, but the :is() pseudo-class provides a concise way to target multiple heading levels in a single selector, avoiding repetitive comma-separated rules. This is the modern idiomatic approach to styling headings as a group.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /* Without :is() — verbose */ h1, h2, h3, h4, h5, h6 { font-family: var(--font-display); line-height: 1.2; } /* With :is() — concise */ :is(h1, h2, h3, h4, h5, h6) { font-family: var(--font-display); line-height: 1.2; } /* Contextual heading selectors */ article :is(h2, h3, h4) { margin-top: 2rem; margin-bottom: 0.5rem; } :is(h1, h2, h3, h4, h5, h6) + p { margin-top: 0; /* Remove top margin from paragraph immediately after a heading */ } The specificity of :is() is determined by its most specific argument — :is(h1, h2, h3) has the same specificity as a single type selector (h1).\n:where() is a zero-specificity equivalent of :is(), useful when you want heading group styles to be easily overridden:\n1 2 3 4 :where(h1, h2, h3, h4, h5, h6) { /* Zero specificity — any later rule can override without needing higher specificity */ text-wrap: balance; } Together, :is() and :where() make heading group selectors both concise and appropriately manageable in the cascade.\n","permalink":"https://www.webstf.nl/baseline/limited/#heading-selectors","tags":null,"title":"Heading pseudo-classes"},{"categories":null,"content":"hyphenate-limit-chars sets the minimum number of characters required before a word can be hyphenated, and the minimum number of characters that must remain before and after the hyphen. This prevents awkward very-short hyphenations.\n1 2 3 4 5 p { hyphens: auto; hyphenate-limit-chars: 6 3 2; /* min-word min-before min-after */ } 6 — word must be at least 6 characters to be hyphenated 3 — at least 3 characters before the hyphen 2 — at least 2 characters after the hyphen 1 2 3 4 hyphenate-limit-chars: auto; /* browser default */ hyphenate-limit-chars: 6; /* min-word only, browser chooses before/after */ hyphenate-limit-chars: 6 3; /* min-word and min-before */ hyphenate-limit-chars: 6 3 2; /* all three */ Without limits, short words like \u0026ldquo;in\u0026rdquo; or \u0026ldquo;to\u0026rdquo; might be hyphenated unnecessarily. Setting a reasonable minimum word length (5–7) prevents this.\n","permalink":"https://www.webstf.nl/baseline/limited/#hyphenate-limit-chars","tags":null,"title":"Hyphenate limit chars"},{"categories":null,"content":"The if() function brings conditional logic directly into CSS property values. It evaluates one or more conditions and returns a different value depending on which condition is true, without requiring separate rules or selectors.\n1 2 3 4 5 6 7 .button { background: if( style(--variant: primary): var(--color-primary); style(--variant: danger): var(--color-danger); else: var(--color-secondary) ); } Conditions can test style queries (custom property values), media features, or supports queries. The else branch provides a fallback when no condition matches.\n1 2 3 4 5 6 7 .layout { grid-template-columns: if( media(width \u0026gt;= 1024px): repeat(3, 1fr); media(width \u0026gt;= 640px): repeat(2, 1fr); else: 1fr ); } if() is particularly powerful in component design systems where a single element needs to vary across multiple states or themes based on custom property values, reducing the number of separate rules needed and keeping variant logic co-located with the property it affects.\n","permalink":"https://www.webstf.nl/baseline/limited/#if","tags":null,"title":"if()"},{"categories":null,"content":"The ime-mode property was intended to control the state of the Input Method Editor (IME) for text input fields. IMEs are the software systems that allow users to compose characters from languages such as Chinese, Japanese, and Korean using standard keyboards. The property could theoretically activate, deactivate, or disable the IME for a given input.\n1 2 3 4 5 6 7 input.latin-only { ime-mode: disabled; } input.cjk-preferred { ime-mode: active; } The available values were auto (default, no change to IME state), normal (normal IME state), active (IME enabled on focus), inactive (IME disabled on focus), and disabled (IME completely disabled for the field).\nime-mode is now considered obsolete and should not be used in new projects. Browser support was always inconsistent — it was primarily implemented in Firefox and Internet Explorer, with limited or no support in other browsers. Modern browsers have removed or deprecated it.\nThe property\u0026rsquo;s goal of controlling input method behaviour for specific fields was never well served by a CSS property, as input method control is more appropriately handled at the application or OS level. Where language-specific input constraints are needed, HTML attributes such as inputmode and lang, combined with appropriate validation, are the recommended approach.\n","permalink":"https://www.webstf.nl/baseline/limited/#ime-mode","tags":null,"title":"ime-mode"},{"categories":null,"content":"initial-letter creates drop caps and raised caps — enlarged first letters of a paragraph that span multiple lines — using pure CSS with proper typographic alignment.\n1 2 3 p::first-letter { initial-letter: 3; } The first letter spans 3 lines of text, sinking into the paragraph.\n1 2 3 p::first-letter { initial-letter: 3 1; /* size sink */ } sink: 1 raises the cap to sit on the first baseline rather than sinking — a \u0026ldquo;raised cap\u0026rdquo;.\n1 2 3 4 5 6 p::first-letter { initial-letter: 4; font-family: \u0026#34;Playfair Display\u0026#34;, serif; color: #c0392b; margin-right: 0.1em; } Simply enlarging the first letter with font-size does not handle baseline alignment across multiple lines. initial-letter positions the glyph correctly.\n","permalink":"https://www.webstf.nl/baseline/limited/#initial-letter","tags":null,"title":"initial-letter"},{"categories":null,"content":"The interpolate-size property opts an element into animating between a fixed length and an intrinsic size keyword such as auto, min-content, or max-content. By default, CSS cannot interpolate to or from these keywords because they are not fixed values — they resolve differently depending on content. interpolate-size: allow-keywords lifts that restriction.\nThe most common use case is animating an element\u0026rsquo;s height from 0 to auto, which was previously impossible in CSS without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 :root { interpolate-size: allow-keywords; } .drawer { height: 0; overflow: hidden; transition: height 0.3s ease; } .drawer.open { height: auto; } Setting interpolate-size: allow-keywords on :root enables the behaviour globally, so all elements on the page can animate to and from intrinsic sizes. It can also be scoped to individual elements if a global opt-in is not desirable.\ninterpolate-size works in concert with calc-size() and transition-behavior to form the full set of tools for intrinsic-size animation in CSS.\n","permalink":"https://www.webstf.nl/baseline/limited/#interpolate-size","tags":null,"title":"interpolate-size"},{"categories":null,"content":"The inverted-colors media feature detects whether the operating system or browser has enabled a color inversion mode — such as macOS\u0026rsquo;s \u0026ldquo;Invert Colors\u0026rdquo; accessibility option or iOS Smart Invert. When active, the OS inverts all colors on screen, which can cause problems for certain types of content.\n1 2 3 4 5 6 7 8 9 10 @media (inverted-colors: inverted) { img, video { filter: invert(1); /* Re-invert images so they look correct */ } .qr-code { filter: invert(1); /* Re-invert to restore black-on-white */ } } The two values are none (no inversion active, the default) and inverted (the OS is inverting colors).\nThe primary use case is restoring images, videos, and other visual content to their original appearance when OS-level color inversion would otherwise make them look wrong. Photographs and illustrations look unusual when inverted — re-inverting them with filter: invert(1) cancels out the OS inversion and restores the original colors.\nQR codes and barcodes are another important case — inverted versions may not be readable by scanners. Maps, diagrams, and branded illustrations may also need to be re-inverted to remain meaningful.\ninverted-colors is an accessibility-oriented media feature with limited but growing browser support. It is most relevant on Apple platforms where color inversion is a common accessibility setting.\n","permalink":"https://www.webstf.nl/baseline/limited/#inverted-colors","tags":null,"title":"inverted-colors media query"},{"categories":null,"content":"justify-self now works in block layout, not just grid and absolute positioning. Applied to a block-level element, it controls that element\u0026rsquo;s alignment along the inline axis — horizontally in left-to-right writing modes — within its containing block.\nPreviously, horizontally aligning a block element within its parent meant using margin: auto tricks or switching to flexbox or grid just for alignment purposes. justify-self makes this a direct, readable property on the element itself.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .container { width: 100%; } .content { width: 60ch; justify-self: center; /* Centre this block without margin: auto */ } .aside { width: 20rem; justify-self: end; /* Push to the right edge */ } Accepted values include start, end, center, stretch, and the same alignment keywords available in flex and grid contexts. Like align-content in block layouts, this reduces the number of situations where a full layout model change is needed just to control positioning.\n","permalink":"https://www.webstf.nl/baseline/limited/#justify-self-block","tags":null,"title":"justify-self-block"},{"categories":null,"content":"Line clamping truncates text to a specified number of lines and appends an ellipsis at the point of truncation. The most widely supported approach uses a combination of prefixed and legacy properties, though a standardised line-clamp property is defined in the CSS Overflow specification.\nThe current cross-browser technique uses -webkit-line-clamp with a set of companion properties:\n1 2 3 4 5 6 .clamped { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; } This limits the element\u0026rsquo;s visible content to three lines, with an ellipsis automatically appended at the cut-off point. Despite the -webkit- prefix, this technique works in all modern browsers including Firefox.\nThe standardised line-clamp property, defined in CSS Overflow Level 4, simplifies this to a single declaration and does not require the display or overflow companion properties:\n1 2 3 .clamped { line-clamp: 3; /* Standardised — browser support still limited */ } The standard property also supports a two-value syntax allowing a custom string to replace the ellipsis:\n1 2 3 .clamped { line-clamp: 3 \u0026#39; [more]\u0026#39;; } Until the standardised property achieves full browser support, the -webkit-box approach remains the reliable production technique. The two can be layered so that browsers adopting the standard form will automatically use it while others fall back.\n","permalink":"https://www.webstf.nl/baseline/limited/#line-clamp","tags":null,"title":"line-clamp"},{"categories":null,"content":"margin-trim instructs a container to discard the margins of its child elements at the container\u0026rsquo;s edges — eliminating the need for :first-child / :last-child hacks to remove unwanted space.\n1 2 3 4 5 6 7 8 9 10 11 .container p { margin-block: 1rem; } /* First and last \u0026lt;p\u0026gt; add unwanted space at container edges */ /* Solution before margin-trim */ .container p:first-child { margin-top: 0; } .container p:last-child { margin-bottom: 0; } /* The new solution */ .container { margin-trim: block; } This automatically trims the margin-block-start of the first child and margin-block-end of the last child.\n1 2 3 4 5 margin-trim: none; /* default — no trimming */ margin-trim: block; /* trim block-start and block-end edges */ margin-trim: inline; /* trim inline-start and inline-end edges */ margin-trim: block-start; /* trim only the block-start edge */ margin-trim: block-end; /* trim only the block-end edge */ How to use\nArticle containers where paragraphs have block margins Card components with padded containers Any component where edge margins cause visual inconsistency ","permalink":"https://www.webstf.nl/baseline/limited/#margin-trim","tags":null,"title":"margin-trim"},{"categories":null,"content":"CSS masonry layout is a two-dimensional layout mode where items are placed into columns but pack upward into the available space left by shorter items in the row above — like bricks laid in a wall, or the Pinterest-style card grid. It eliminates the uneven gaps that appear in a standard grid when items have varying heights.\nMasonry is defined as part of CSS Grid Layout and is enabled by setting one of the grid axes to masonry:\n1 2 3 4 5 6 .masonry-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: masonry; gap: 1rem; } With grid-template-rows: masonry, items are placed into columns defined by grid-template-columns, but the row sizing is handled by the masonry algorithm — each item sits as close to the top as possible, filling gaps left by shorter siblings.\nThe axis can be flipped for a horizontal masonry layout:\n1 2 3 4 5 .horizontal-masonry { display: grid; grid-template-columns: masonry; grid-template-rows: repeat(3, 1fr); } Items can still be explicitly placed on the non-masonry axis using grid-column (for vertical masonry) and can span multiple columns.\nCSS masonry was still in the experimental phase as of early 2026, available behind flags in some browsers. The feature has been actively debated — an alternative proposal using display: masonry was also under consideration — so the final syntax may differ from the current specification. JavaScript-based masonry libraries (such as Masonry.js) remain the production-ready alternative in the interim.\n","permalink":"https://www.webstf.nl/baseline/limited/#masonry","tags":null,"title":"Masonry"},{"categories":null,"content":"CSS numeric factory functions — CSS.px(), CSS.em(), CSS.percent(), CSS.deg(), and others — are part of the CSS Typed OM API. They create typed CSSUnitValue objects for safe, readable programmatic CSS manipulation.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 CSS.px(10) // { value: 10, unit: \u0026#39;px\u0026#39; } CSS.em(1.5) // { value: 1.5, unit: \u0026#39;em\u0026#39; } CSS.rem(2) // { value: 2, unit: \u0026#39;rem\u0026#39; } CSS.percent(50) // { value: 50, unit: \u0026#39;percent\u0026#39; } CSS.vw(100) // { value: 100, unit: \u0026#39;vw\u0026#39; } CSS.vh(100) // { value: 100, unit: \u0026#39;vh\u0026#39; } CSS.deg(45) // { value: 45, unit: \u0026#39;deg\u0026#39; } CSS.turn(0.5) // { value: 0.5, unit: \u0026#39;turn\u0026#39; } CSS.s(0.3) // { value: 0.3, unit: \u0026#39;s\u0026#39; } CSS.number(1.5) // { value: 1.5, unit: \u0026#39;number\u0026#39; } /* Setting Styles */ const el = document.querySelector(\u0026#39;.box\u0026#39;); el.attributeStyleMap.set(\u0026#39;width\u0026#39;, CSS.px(200)); el.attributeStyleMap.set(\u0026#39;transform\u0026#39;, new CSSTranslate(CSS.px(10), CSS.percent(50)) ); /* Arithmetic */ const width = CSS.px(100).add(CSS.px(50)); // CSS.px(150) Why use these?\nType safety — mismatched units are caught at assignment No string concatenation: CSS.px(val) vs val + \u0026quot;px\u0026quot; Composable with other Typed OM objects like CSSTranslate ","permalink":"https://www.webstf.nl/baseline/limited/#numeric-factory-functions","tags":null,"title":"Numeric factory functions"},{"categories":null,"content":"The overflow-clip-margin property controls how far outside an element\u0026rsquo;s border box content is allowed to paint before being clipped, when overflow: clip is set. It defines an outset from the element\u0026rsquo;s padding edge within which overflow is still permitted to render.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .clipped { overflow: clip; overflow-clip-margin: 20px; /* Allow 20px of overflow before clipping */ } .tight { overflow: clip; overflow-clip-margin: 0; /* Default: clip exactly at the border edge */ } .generous { overflow: clip; overflow-clip-margin: content-box 1rem; } overflow: clip is similar to overflow: hidden in that it prevents scrolling and clips content, but it does not establish a new block formatting context — making it more predictable in certain layout situations. overflow-clip-margin extends the clip boundary outward by the specified amount, giving content a margin of space to paint into before being cut off.\nThis is particularly useful for elements where a drop shadow, outline, or other ink overflow effect needs to bleed slightly outside the element\u0026rsquo;s box without establishing a full new stacking or formatting context. Without overflow-clip-margin, overflow: clip cuts content off sharply at the border edge, which can unintentionally hide box shadows or focus rings.\nThe value accepts a length, or a geometry box keyword (content-box, padding-box, border-box) followed by an optional length, specifying the reference box from which the outset is measured.\n","permalink":"https://www.webstf.nl/baseline/limited/#overflow-clip-margin","tags":null,"title":"overflow-clip-margin"},{"categories":null,"content":"overflow: overlay displays scrollbars that float over the content rather than occupying layout space, preserving the element\u0026rsquo;s full width. It is now an alias for overflow: auto in most modern browsers.\n1 2 3 .scrollable { overflow: overlay; } On systems with overlay scrollbars (macOS, iOS, some Linux), this produces scrollbars that appear on hover without shifting the layout.\noverflow: overlay was a non-standard value introduced by WebKit. It has since been standardised as an alias for overflow: auto in the CSS spec — browsers that show overlay scrollbars will do so automatically with auto.\nOn macOS and mobile devices, overflow: auto already shows overlay scrollbars. For Windows-style persistent scrollbars, use the scrollbar-gutter property instead:\n1 2 3 4 .scrollable { overflow: auto; scrollbar-gutter: stable; /* reserve space for scrollbar even when not scrolling */ } overflow: overlay is deprecated. New projects should use overflow: auto and scrollbar-gutter for reliable cross-platform behaviour.\n","permalink":"https://www.webstf.nl/baseline/limited/#overflow-overlay","tags":null,"title":"overflow: overlay"},{"categories":null,"content":"The overlay property controls whether an element is rendered in the browser\u0026rsquo;s \u0026ldquo;top layer\u0026rdquo; — the layer used for \u0026lt;dialog\u0026gt;, popovers, and fullscreen elements — and crucially, whether it stays there during exit transitions.\nWhen a dialog closes, it is normally removed from the top layer immediately, cutting off any exit animation before it completes.\n1 2 3 4 5 6 7 8 9 dialog { transition: opacity 0.3s, overlay 0.3s allow-discrete; opacity: 1; } dialog:not([open]) { opacity: 0; overlay: none; } overlay: allow-discrete keeps the element in the top layer until its transition finishes.\nFull animated dialog dismissal uses three features together:\n1 2 3 4 5 6 dialog { transition: display 0.3s allow-discrete, overlay 0.3s allow-discrete, opacity 0.3s; } display — deferred so element stays visible during fade overlay — deferred so element stays on top during fade opacity — the actual visible animation ","permalink":"https://www.webstf.nl/baseline/limited/#overlay","tags":null,"title":"overlay"},{"categories":null,"content":"The page-break-before, page-break-after, and page-break-inside properties are legacy aliases for the modern break-before, break-after, and break-inside properties. They were introduced for controlling page breaks in print layouts and are now superseded by the generalised fragmentation properties, which work across paged media, multi-column layouts, and CSS regions.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* Legacy syntax */ h2 { page-break-before: always; } .no-break { page-break-inside: avoid; } figure { page-break-after: avoid; } /* Modern equivalents */ h2 { break-before: page; } .no-break { break-inside: avoid; } figure { break-after: avoid; } The legacy properties accept values including auto, always, avoid, left, right, and page. The modern break-* properties accept these and additional values that apply to column and region contexts: column, avoid-column, avoid-page, and others.\nBrowsers continue to support the legacy properties for backwards compatibility, and they map directly to their break-* equivalents. For new print stylesheets, the modern break-before, break-after, and break-inside properties are preferred as they are more versatile and better reflect the unified fragmentation model.\n","permalink":"https://www.webstf.nl/baseline/limited/#page-break-aliases","tags":null,"title":"Page break aliases"},{"categories":null,"content":"Page selectors are pseudo-classes used within @page rules to target specific pages in a paged document — such as the first page, left-hand pages, right-hand pages, and blank pages. They allow different margin, size, and content settings to be applied per page type in print layouts.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @page { margin: 2cm; } @page :first { margin-top: 4cm; /* Extra space on the title page */ } @page :left { margin-left: 3cm; margin-right: 2cm; } @page :right { margin-left: 2cm; margin-right: 3cm; } @page :blank { @top-center { content: none; /* No header on intentionally blank pages */ } } :first matches the first page of the document. :left and :right match left-hand and right-hand pages in a double-sided (spread) layout — which is :left or :right depends on the writing direction and the @page size configuration. :blank matches pages that are empty due to forced page breaks.\nNamed pages extend this system further — the page property on an element associates it with a named @page rule, allowing entirely different page configurations for different sections of a document:\n1 2 3 4 5 6 7 @page chapter-start { margin-top: 6cm; } .chapter-heading { page: chapter-start; } Page selectors are primarily relevant for print stylesheets and PDF generation, where precise control over individual page types is a typographic requirement.\n","permalink":"https://www.webstf.nl/baseline/limited/#page-selectors","tags":null,"title":"Page selectors"},{"categories":null,"content":"The page-orientation descriptor, used inside @page rules, controls the rotation of a printed page. It allows individual pages or page groups within a document to be oriented differently — for example, rotating a single wide table to landscape while the rest of the document remains portrait.\n1 2 3 4 5 6 7 8 9 10 11 12 @page { size: A4 portrait; } @page landscape-page { size: A4 landscape; page-orientation: rotate-left; } .wide-table { page: landscape-page; } The rotate-left and rotate-right values rotate the page content accordingly, while upright is the default with no rotation applied. The page property on the element associates it with a named @page rule, so different parts of the document can use different page configurations.\npage-orientation fills a long-standing gap in CSS print layout, where mixed portrait and landscape pages in a single document previously required separate files or complex workarounds.\n","permalink":"https://www.webstf.nl/baseline/limited/#page-orientation","tags":null,"title":"page-orientation"},{"categories":null,"content":"The paint() function is part of the CSS Houdini CSS Painting API. It calls a custom Paint Worklet — a JavaScript class that implements the paint() method — to generate an image that can be used as a background, border image, or any other property that accepts an image value. It brings programmable, canvas-like drawing directly into CSS.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* Registering the worklet */ CSS.paintWorklet.addModule(\u0026#39;my-painter.js\u0026#39;); /* Using it in CSS */ .element { background: paint(my-painter); } .custom-border { border-image: paint(fancy-border) 30; } .with-params { --paint-color: oklch(60% 0.2 250); --paint-size: 8; background: paint(dots-painter); } The Paint Worklet script defines a class with a static inputProperties array (listing which CSS custom properties it reads) and a paint() method that receives a canvas-like rendering context, the element\u0026rsquo;s geometry, and its custom property values:\n1 2 3 4 5 6 7 8 9 // my-painter.js registerPaint(\u0026#39;my-painter\u0026#39;, class { static get inputProperties() { return [\u0026#39;--paint-color\u0026#39;]; } paint(ctx, geom, properties) { ctx.fillStyle = properties.get(\u0026#39;--paint-color\u0026#39;).toString(); ctx.fillRect(0, 0, geom.width, geom.height); } }); Paint Worklets run off the main thread and re-execute whenever the element\u0026rsquo;s size or specified custom properties change, enabling responsive custom graphics without JavaScript event listeners. The API is part of CSS Houdini — browser support is currently limited to Chromium-based browsers.\n","permalink":"https://www.webstf.nl/baseline/limited/#paint","tags":null,"title":"paint()"},{"categories":null,"content":"The prefers-reduced-data media feature detects whether the user has requested that the browser use less data — for example, when they have enabled a data-saver mode in their operating system or browser, or when they are on a metered connection. It allows CSS to reduce the amount of data downloaded as part of rendering the page.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 @media (prefers-reduced-data: reduce) { .hero { background-image: none; /* Skip the large background photo */ background-color: var(--color-primary); } .video-poster { display: none; } .webfont { font-family: system-ui, sans-serif; /* Fall back to system font */ } } The two values are no-preference (the default, no data-saving request) and reduce (the user or their network environment has indicated a preference for reduced data usage).\nPractical applications include skipping large background images, deferring non-critical web fonts in favour of system fonts, omitting decorative video or animation assets, and loading lower-resolution images. These changes can significantly reduce page weight for users on slow or metered connections.\nAs of early 2026, prefers-reduced-data had limited browser support and was not yet implemented in most major browsers without flags, though it is defined in the Media Queries Level 5 specification. It is worth implementing proactively so the styles are in place when support broadens.\n","permalink":"https://www.webstf.nl/baseline/limited/#prefers-reduced-data","tags":null,"title":"prefers-reduced-data media query"},{"categories":null,"content":"The prefers-reduced-transparency media query detects whether the user has requested reduced transparency in their OS accessibility settings. Some users find frosted glass and translucent UI elements distracting or difficult to read.\n1 2 3 4 5 6 7 8 9 10 11 @media (prefers-reduced-transparency: reduce) { .glass-panel { background: rgba(255, 255, 255, 0.95); /* nearly opaque */ backdrop-filter: none; } .tooltip { background: #333; /* solid fallback */ opacity: 1; } } 1 2 3 @media (prefers-reduced-transparency: no-preference) { /* User has not requested reduced transparency — use full effects */ } When reduce is active:\nReplace backdrop-filter: blur() with a solid or near-solid background Increase opacity of translucent overlays Simplify layered semi-transparent UI Users with vestibular disorders, visual processing difficulties, or cognitive conditions may enable this setting. It is available on macOS (Accessibility → Display) and iOS.\n","permalink":"https://www.webstf.nl/baseline/limited/#prefers-reduced-transparency","tags":null,"title":"prefers-reduced-transparency media query"},{"categories":null,"content":"The progress() function calculates where a value falls between a minimum and maximum, returning a number between 0 and 1 that represents its position in that range. It is the CSS equivalent of a linear normalisation calculation — the same operation that JavaScript developers often write as (value - min) / (max - min).\n1 2 3 4 5 6 7 8 .element { --value: 650; --min: 320; --max: 1200; /* Returns a 0–1 ratio of where --value sits in the range */ opacity: progress(var(--value), var(--min), var(--max)); } The result can be used directly as a unitless value (for opacity, font-weight, etc.) or multiplied to produce values in any unit. Combined with @property for typed custom properties and calc(), progress() enables expressive range-based calculations directly in CSS.\nIt pairs naturally with scroll-driven animations, where you might want to express a style as a proportion of scroll progress, and with @function for building reusable interpolation utilities.\n","permalink":"https://www.webstf.nl/baseline/limited/#progress-function","tags":null,"title":"progress()"},{"categories":null,"content":"The random() function generates a random value within a specified range, directly in CSS. It accepts a minimum and maximum value and returns a random number between them, optionally snapped to a given step interval.\n1 2 3 4 5 6 7 8 9 10 .particle { width: random(4px, 20px); height: random(4px, 20px); opacity: random(0.3, 1); } .item { /* Random value snapped to 10px increments */ margin-top: random(10px, 60px, by 10px); } By default, each element gets its own independently randomised value. A random-caching-key parameter can be used to share a random value across multiple elements or properties — useful when you want a group of elements to share the same random offset, or when you need the same random value applied to multiple properties of a single element.\n1 2 3 4 5 .card { --r: random(0deg, 5deg, by 1deg, cache random-tilt); rotate: var(--r); outline-offset: calc(var(--r) * 2); } random() is a generative design tool built into CSS, enabling organic variation in layouts, animations, and visual treatments without JavaScript.\n","permalink":"https://www.webstf.nl/baseline/limited/#random-function","tags":null,"title":"random()"},{"categories":null,"content":"Style container queries now support range syntax for querying numeric custom property values, using comparison operators rather than equality checks. This mirrors the range syntax already available for size container queries and media queries, and allows you to apply styles when a custom property value falls within, above, or below a threshold.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @container style(--size \u0026gt; 2) { .label { font-size: 1.25rem; } } @container style(1 \u0026lt;= --level \u0026lt;= 3) { .indicator { background: var(--color-warning); } } @container style(--priority \u0026gt;= 5) { .item { font-weight: bold; border-left: 4px solid var(--color-urgent); } } Without range syntax, style queries could only check whether a custom property equalled a specific value. Range syntax makes it practical to build components that respond to a spectrum of values — priority levels, size scales, quantity thresholds — without requiring a separate query for each discrete value.\n","permalink":"https://www.webstf.nl/baseline/limited/#style-query-range-syntax","tags":null,"title":"Range syntax for style queries"},{"categories":null,"content":"The reading-flow property controls the order in which focus and reading tools (such as screen readers and keyboard navigation) traverse the items in a flex or grid container, independently of their visual order. This addresses a long-standing accessibility concern where CSS layout properties like order, flex-direction: row-reverse, or grid placement can produce a visual order that differs from the DOM order — and therefore from the tab/focus order that assistive technologies follow.\n1 2 3 4 5 6 7 8 9 10 11 .grid { display: grid; grid-template-columns: repeat(3, 1fr); reading-flow: grid-rows; /* Follow visual row order */ } .flex-reversed { display: flex; flex-direction: row-reverse; reading-flow: flex-visual; /* Follow visual left-to-right order */ } The grid-rows value makes focus follow the visual row-by-row order of grid items. grid-columns follows column order. flex-visual follows the visual order of flex items regardless of flex-direction or order. flex-flow follows the flex flow direction.\nWithout reading-flow, the only way to align focus order with a rearranged visual layout was to reorder the DOM itself — which has its own problems — or use tabindex on every item, which is fragile and verbose.\n","permalink":"https://www.webstf.nl/baseline/limited/#reading-flow","tags":null,"title":"reading-flow"},{"categories":null,"content":"The resize property controls whether an element can be resized by the user, and in which directions. By default, browsers make \u0026lt;textarea\u0026gt; elements resizable — resize allows this behaviour to be controlled or extended to other elements.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 textarea { resize: vertical; /* Only vertical resizing (default for textarea) */ } .resizable-box { overflow: auto; /* Required for resize to work on non-textarea elements */ resize: both; min-width: 200px; min-height: 100px; } .no-resize { resize: none; } .horizontal-only { overflow: hidden; resize: horizontal; } The accepted values are none, both, horizontal, and vertical. For resize to work on elements other than \u0026lt;textarea\u0026gt;, the element must have its overflow set to a value other than visible — typically auto, hidden, or scroll.\nresize: none is commonly applied to textareas in designs where the default resize handle would break a layout, though removing it reduces usability for users who need more space to write. field-sizing: content is now a better alternative for textareas that should grow with their content automatically, removing the need for manual resizing entirely.\nThe resize handle appears in the element\u0026rsquo;s bottom-right corner by default and can be styled via the ::-webkit-resizer pseudo-element in WebKit-based browsers.\n","permalink":"https://www.webstf.nl/baseline/limited/#resize","tags":null,"title":"resize"},{"categories":null,"content":"Rhythmic sizing is a set of CSS features designed to keep vertical spacing consistent with a typographic baseline grid — ensuring that the height of every element, including images, figures, and other non-text content, snaps to a multiple of the base line height. This preserves vertical rhythm across the entire page.\nThe core property is line-height-step, which causes block-level elements to round their block size up to the nearest multiple of the specified value:\n1 2 3 4 5 6 7 8 :root { --line-height: 1.5rem; line-height: var(--line-height); } html { line-height-step: 1.5rem; /* Snap all block heights to multiples of 1.5rem */ } With line-height-step set, any block element whose content does not exactly fill a whole number of line-height units will have padding added at the bottom to round up to the next step. This keeps baseline alignment consistent regardless of content.\n1 2 3 4 figure { /* Image height will be rounded up to the nearest multiple of the line-height step */ line-height-step: 1.5rem; } Rhythmic sizing works alongside the rlh unit, which represents the root element\u0026rsquo;s line height — making it straightforward to express spacings and sizes as multiples of the baseline without manual calculations.\nAs of early 2026, line-height-step had limited browser support and was not yet available in production use without workarounds. The feature is defined in the CSS Rhythmic Sizing specification. In the interim, careful use of rlh units and consistent spacing tokens approximates the same result.\n","permalink":"https://www.webstf.nl/baseline/limited/#rhythmic-sizing","tags":null,"title":"Rhythmic sizing"},{"categories":null,"content":"sibling-count() and sibling-index() are CSS functions that return information about an element\u0026rsquo;s position among its siblings. sibling-index() returns the element\u0026rsquo;s one-based index within its parent, and sibling-count() returns the total number of siblings. Both return integers usable in calc() and other math contexts.\nThese functions unlock a category of dynamic styling that previously required either inline styles set by JavaScript, or elaborate :nth-child workarounds that only worked for a fixed number of items.\n1 2 3 4 5 6 7 .item { /* Stagger animation delays based on position */ animation-delay: calc(sibling-index() * 0.1s); /* Spread hue across all items */ color: hsl(calc(sibling-index() / sibling-count() * 360deg) 70% 50%); } 1 2 3 4 .grid { /* Adjust column count based on how many items there are */ grid-template-columns: repeat(sibling-count(), 1fr); } The ability to reference an element\u0026rsquo;s position and the total count directly in CSS removes a whole class of JavaScript-assisted styling patterns, particularly for lists, card grids, animated sequences, and data visualisations.\n","permalink":"https://www.webstf.nl/baseline/limited/#sibling-count","tags":null,"title":"sibling-count() and sibling-index()"},{"categories":null,"content":"The speak property is a CSS aural property that controls whether an element\u0026rsquo;s content should be spoken aloud by a speech synthesiser. It was part of the CSS2 Aural Style Sheets specification, which defined a full set of properties for controlling speech output. The property has since been moved to the CSS Speech module.\n1 2 3 4 5 6 7 8 9 10 11 .screen-only { speak: none; /* Do not read this element aloud */ } .read-aloud { speak: normal; /* Default: read content aloud */ } .spell-out { speak: spell-out; /* Read content letter by letter */ } normal is the default — the element\u0026rsquo;s content is read using normal language rules. none suppresses speech output entirely for the element, similar to display: none for visual rendering. spell-out causes the content to be spelled out character by character, which is sometimes useful for codes, abbreviations, or strings where the default pronunciation would be incorrect.\nThe speak property should be distinguished from the speak-as descriptor used in @counter-style rules, which controls how counter values are pronounced by screen readers.\nIn practice, most of the CSS aural properties including speak have poor support in mainstream browsers, as they were primarily designed for dedicated speech browsers and assistive technology environments. For accessibility control over what screen readers announce, ARIA attributes — particularly aria-hidden, aria-label, and aria-live — are the reliable and widely supported alternative.\n","permalink":"https://www.webstf.nl/baseline/limited/#speak","tags":null,"title":"speak"},{"categories":null,"content":"The speak-as descriptor is used inside @counter-style rules to define how a custom counter style should be spoken aloud by speech synthesis systems — primarily screen readers. It maps the visual counter representation to an appropriate spoken form.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @counter-style thumbs { system: cyclic; symbols: \u0026#39;\\1F44D\u0026#39;; suffix: \u0026#39; \u0026#39;; speak-as: bullets; /* Speak as a generic list item, not as the emoji name */ } @counter-style roman-upper { system: additive; additive-symbols: 1000 M, 900 CM, 500 D, 400 CD, 100 C, 90 XC, 50 L, 40 XL, 10 X, 9 IX, 5 V, 4 IV, 1 I; speak-as: numbers; /* Speak the numeric value rather than the Roman numeral */ } @counter-style custom-alpha { system: alphabetic; symbols: A B C D E F; speak-as: auto; /* Default: infer from the counter system */ } The accepted values are auto (the default, which infers appropriate speech from the counter system), bullets (treated as an unordered list item), numbers (the numeric value is spoken), words (the symbol is spoken as a word), spell-out (the symbol is spelled out letter by letter), and a \u0026lt;counter-style-name\u0026gt; reference to another counter style whose speech rules should be used.\nspeak-as is relevant for accessibility in documents that use CSS counter styles for list markers or generated content, ensuring that the spoken representation is meaningful even when the visual symbol — an emoji, a custom glyph, or an obscure alphabetic system — would not be naturally readable by a speech engine.\n","permalink":"https://www.webstf.nl/baseline/limited/#speak-as","tags":null,"title":"speak-as"},{"categories":null,"content":"stretch is a standardised keyword for sizing properties like width, height, min-width, and max-width. It makes an element size itself to fill the available space in its containing block, equivalent to how a block element behaves horizontally by default — but applicable to any axis and any element type.\nPreviously, this behaviour was only achievable via the vendor-prefixed -webkit-fill-available or -moz-available, which were inconsistent across browsers and not part of any standard.\n1 2 3 4 5 6 7 8 .button { width: stretch; /* Fill the available inline space */ } .panel { height: stretch; /* Fill available block space */ min-height: stretch; } stretch is particularly useful for inline elements or replaced elements (like \u0026lt;button\u0026gt; and \u0026lt;input\u0026gt;) that do not naturally fill their container width, and for elements in flex or grid contexts where you want an item to expand without using flex: 1 or align-self: stretch.\nIt can also be used in fit-content alternatives and alongside min-width and max-width to set a responsive size range anchored to the available space.\n","permalink":"https://www.webstf.nl/baseline/limited/#stretch","tags":null,"title":"stretch"},{"categories":null,"content":"The text-box property is a shorthand for text-box-trim and text-box-edge, which together control the trimming of the space above and below text within its line box. By default, text in CSS carries extra space above capital letters (between the top of the em square and the cap height) and below the baseline (for descenders). This built-in spacing makes precise vertical alignment and optical centering difficult.\ntext-box lets you trim this space, so the measured box of a text element corresponds to the actual visual ink bounds of the letters rather than the theoretical em square.\n1 2 3 4 5 6 7 8 9 .button { /* Trim space above cap height and below baseline */ text-box: trim-both cap alphabetic; padding: 1rem 1.5rem; } h1 { text-box: trim-start cap; } The first value after trim-both / trim-start / trim-end is the over edge (top) metric — cap for cap height, ex for x-height, text for the full ascender. The second value is the under edge (bottom) metric — alphabetic for the baseline (excluding descenders), text for the full descender.\nThis makes it significantly easier to achieve optically centered text in buttons, badges, and headings without compensatory padding hacks, and to align text precisely with adjacent elements like icons.\n","permalink":"https://www.webstf.nl/baseline/limited/#text-box","tags":null,"title":"text-box"},{"categories":null,"content":"The blink value for text-decoration-line was intended to make text flash on and off, similar to the behaviour of the old \u0026lt;blink\u0026gt; HTML element. It is defined in the CSS specification but browsers are explicitly permitted to ignore it — and all modern browsers do exactly that, rendering the text without any blinking effect.\n1 2 3 .blinking { text-decoration-line: blink; /* Ignored by all modern browsers */ } The blink value was deprecated because blinking text is widely considered a serious accessibility problem. It can trigger seizures in users with photosensitive epilepsy and is extremely distracting for users with attention or cognitive disabilities. The Web Content Accessibility Guidelines (WCAG) prohibit content that flashes more than three times per second.\nThe valid and useful values of text-decoration-line are none, underline, overline, and line-through, which can be combined:\n1 2 3 4 5 6 7 8 9 10 11 .underline { text-decoration-line: underline; } .strikethrough { text-decoration-line: line-through; } .overline-and-underline { text-decoration-line: overline underline; } text-decoration-line is one of the longhands of the text-decoration shorthand, alongside text-decoration-color, text-decoration-style, and text-decoration-thickness.\n","permalink":"https://www.webstf.nl/baseline/limited/#text-decoration-line-blink","tags":null,"title":"text-decoration-line: blink"},{"categories":null,"content":"The text-spacing-trim property controls the trimming of intrinsic spacing built into CJK (Chinese, Japanese, Korean) punctuation characters. Many CJK punctuation glyphs include whitespace as part of their glyph box — for example, an ideographic full stop has space on one or both sides to maintain rhythm in CJK text. When mixed with other punctuation or at the start and end of lines, this built-in spacing can create excessive gaps.\ntext-spacing-trim lets you remove this spacing in a typographically appropriate way, following the rules of East Asian typography standards.\n1 2 3 4 5 6 7 article { text-spacing-trim: trim-start; } .cjk-body { text-spacing-trim: space-all; } The trim-start value removes the spacing at the start of lines, which is a common requirement for CJK body text. space-all preserves all spacing, while trim-all removes it throughout. The feature works alongside text-autospace (which handles spacing between CJK and non-CJK characters) to give fine-grained typographic control over mixed-script and CJK-primary text.\n","permalink":"https://www.webstf.nl/baseline/limited/#text-spacing-trim","tags":null,"title":"text-spacing-trim"},{"categories":null,"content":"text-wrap: pretty optimises line breaking to avoid leaving a single word orphaned on the last line of a paragraph, improving typographic quality in body text without the performance cost of full balancing.\n1 2 3 p { text-wrap: pretty; } What is the difference from text-wrap: balance\nbalance — makes all lines equal length; capped at ~6 lines; good for headings pretty — avoids orphans on the last line; works on any length of text; good for body copy 1 2 h1, h2, h3 { text-wrap: balance; } /* headings */ p { text-wrap: pretty; } /* paragraphs */ text-wrap: pretty is less expensive than balance because it only optimises the last line rather than recalculating all line breaks. It is safe to apply to body text.\nAn orphan is a single word (or very short last line) at the end of a paragraph — common in justified text or narrow containers:\n\u0026ldquo;The quick brown fox jumps over the lazy dog.\u0026rdquo;\npretty avoids this by reflowing the preceding line.\n","permalink":"https://www.webstf.nl/baseline/limited/#text-wrap-pretty","tags":null,"title":"text-wrap: pretty"},{"categories":null,"content":"Time-relative pseudo-classes match elements based on their position relative to the current playback point in a timed media presentation — such as a WebVTT caption track or a Synchronized Multimedia Integration Language (SMIL) document. They provide a CSS hook for styling content that is currently active, past, or upcoming in a time-based sequence.\nThe three pseudo-classes are :current, :past, and :future.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* Currently active caption */ :current(p) { background: rgba(0, 0, 0, 0.75); color: white; } /* Already spoken captions */ :past(p) { opacity: 0.5; color: #aaa; } /* Upcoming captions */ :future(p) { opacity: 0.3; } :current matches the element or elements that are currently being presented — for example, the active cue in a subtitle track. It can take a selector argument to narrow which elements it targets. :past matches elements whose time range has already passed. :future matches elements yet to be presented.\nThese pseudo-classes are defined in the CSS Selectors specification with their primary use case being WebVTT cue styling in \u0026lt;track\u0026gt; elements. Browser support is limited and they are primarily relevant in specialist media player contexts rather than general web development.\n","permalink":"https://www.webstf.nl/baseline/limited/#time-relative-selectors","tags":null,"title":"Time-relative pseudo-selectors"},{"categories":null,"content":"The user-select property controls whether the user can select text in an element. It is commonly used to prevent accidental text selection on interactive UI elements, or to make certain content non-selectable.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .button { user-select: none; /* Clicking doesn\u0026#39;t select button text */ } .draggable { user-select: none; /* Dragging doesn\u0026#39;t select text */ } .code-block { user-select: all; /* One click selects the entire block */ } .normal-text { user-select: text; /* Default: text is selectable */ } none prevents the element\u0026rsquo;s text from being selected. text (the default) allows normal text selection. all makes the entire element\u0026rsquo;s content select as a unit on a single click — useful for code snippets, URLs, or any content that is typically copied in full. contain (formerly element in some browsers) confines selection to within the element\u0026rsquo;s bounds.\nuser-select: none does not prevent copying via keyboard shortcuts if the element is focused, and it does not affect programmatic selection via JavaScript (window.getSelection()). It only affects mouse and touch-based selection.\nThe property was long prefixed as -webkit-user-select for WebKit and Blink browsers. The unprefixed version is now widely supported, but for maximum compatibility with older browsers the prefixed form is sometimes still included alongside the standard property.\n","permalink":"https://www.webstf.nl/baseline/limited/#user-select","tags":null,"title":"user-select"},{"categories":null,"content":"The widows and orphans properties control how paragraph text is split across page breaks, column breaks, or region breaks in paged and multi-column layouts. They specify the minimum number of lines that must remain together at the top or bottom of a fragment.\nAn orphan is one or more lines left alone at the bottom of a page before a break. A widow is one or more lines left alone at the top of a page after a break. Both are considered typographically poor and are avoided in quality print design.\n1 2 3 4 p { orphans: 3; /* At least 3 lines must remain at the bottom before a break */ widows: 3; /* At least 3 lines must appear at the top after a break */ } When a paragraph would be split in a way that leaves fewer lines than the specified minimum, the browser moves the break point to keep the required number of lines together, pushing more content to the next page or column.\nBoth properties accept integer values and default to 2. They are inherited, so setting them on body or a container applies throughout the document. Their effect is most apparent in print stylesheets and CSS multi-column layouts — in standard single-column screen layouts there are no page breaks, so the properties have no visual impact.\nNote that widows and orphans address line-level breaks within paragraphs. The related break-inside, break-before, and break-after properties control breaks at the element level.\n","permalink":"https://www.webstf.nl/baseline/limited/#widows-orphans","tags":null,"title":"Widows and orphans"},{"categories":null,"content":"word-break: auto-phrase instructs the browser to break lines at natural phrase boundaries rather than at arbitrary character positions, improving readability in CJK and mixed-script text.\n1 2 3 p { word-break: auto-phrase; } Standard word breaking in CJK text can split grammatical phrases arbitrarily. auto-phrase uses linguistic analysis to identify natural pause points and break lines there, much like a human typesetter would.\nWithout auto-phrase:\n東京都は日本の首都です。\nWith auto-phrase (breaks at natural phrase boundary):\n東京都は日本の首都です。\nThe primary benefit today is for Japanese text. The browser\u0026rsquo;s line-breaking dictionary determines what counts as a phrase boundary.\n1 2 3 4 word-break: normal; /* default */ word-break: break-all; /* break anywhere */ word-break: keep-all; /* no mid-word breaks for CJK */ word-break: auto-phrase; /* break at natural phrases */ ","permalink":"https://www.webstf.nl/baseline/limited/#word-break-auto-phrase","tags":null,"title":"word-break: auto-phrase"},{"categories":null,"content":"The word-break property controls how words break when they overflow their container. The break-word value — now better expressed as overflow-wrap: break-word — forces long unbreakable strings such as URLs, long technical identifiers, or strings without natural break points to wrap rather than overflow.\n1 2 3 4 5 6 7 8 .user-content { word-break: break-word; /* Legacy value, widely supported */ } /* Modern equivalent, preferred */ .user-content { overflow-wrap: break-word; } word-break: break-word was a non-standard value originally introduced by Internet Explorer and subsequently adopted across browsers for compatibility. It behaves identically to overflow-wrap: break-word — breaking words only when they would otherwise overflow, rather than breaking all words aggressively. The overflow-wrap property is the standardised version of what was historically also called word-wrap.\nThe standard word-break values are normal (default), break-all (breaks at any character, not just at overflow, which affects all words not just long ones), and keep-all (prevents breaks in CJK text). break-word was never in the specification as a value for word-break, though browsers honour it as an alias.\nFor new code, overflow-wrap: break-word is preferred. For maximum compatibility with older browsers in legacy codebases, both are sometimes declared together:\n1 2 3 4 .safe { word-break: break-word; /* Legacy */ overflow-wrap: break-word; /* Standard */ } ","permalink":"https://www.webstf.nl/baseline/limited/#word-break-break-word","tags":null,"title":"word-break: break-word"},{"categories":null,"content":"SVG 1.1 defined its own set of writing-mode values — lr, lr-tb, rl, rl-tb, tb, and tb-rl — which predate the CSS Writing Modes specification and used different naming conventions. These values were adopted into CSS for SVG compatibility but are now considered legacy and deprecated in favour of the standard CSS values.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* SVG 1.1 legacy values (deprecated) */ text { writing-mode: tb-rl; /* Top to bottom, right to left — now: vertical-rl */ } text { writing-mode: lr-tb; /* Left to right, top to bottom — now: horizontal-tb */ } /* Modern CSS equivalents */ text { writing-mode: vertical-rl; } text { writing-mode: horizontal-tb; } The mapping between legacy and modern values is straightforward: lr and lr-tb map to horizontal-tb; rl and rl-tb also map to horizontal-tb (with direction: rtl for right-to-left); tb and tb-rl map to vertical-rl.\nModern browsers continue to support the legacy SVG values for backwards compatibility, but they should not be used in new code. The CSS Writing Modes values (horizontal-tb, vertical-rl, vertical-lr, sideways-rl, sideways-lr) are the standard and work consistently in both HTML and SVG contexts.\nWhen authoring inline SVG within HTML documents, the CSS writing mode properties apply and the modern values should be used.\n","permalink":"https://www.webstf.nl/baseline/limited/#writing-mode-svg-values","tags":null,"title":"writing-mode SVG 1.1 values"},{"categories":null,"content":"background-attachment controls whether a background image scrolls with the element\u0026rsquo;s contents, stays fixed relative to the viewport, or moves with the element\u0026rsquo;s local scroll container.\n1 2 3 background-attachment: scroll; /* default — moves with the element */ background-attachment: fixed; /* stays fixed to the viewport */ background-attachment: local; /* moves with the element\u0026#39;s own scroll */ 1 2 3 4 5 6 7 8 /* Parallax effect with position: fixed */ .hero { background-image: url(\u0026#34;landscape.jpg\u0026#34;); background-attachment: fixed; background-size: cover; background-position: center; height: 80vh; } As the user scrolls, the content moves but the background stays put — creating a classic parallax effect.\n1 2 3 4 5 /* Background scrolls with the div\u0026#39;s own overflow scroll */ .scrollable { background-attachment: local; } /* Background stays fixed within the div even when content scrolls */ .fixed-bg { background-attachment: scroll; } background-attachment: fixed forces the browser to repaint the background on every scroll, which can be expensive. For smoother parallax effects, use transform or the Intersection Observer API instead.\n","permalink":"https://www.webstf.nl/baseline/limited/#background-attachment","tags":null,"title":"Background attachment"},{"categories":null,"content":"COLRv1 is a font format that enables color fonts with gradients, compositing, and detailed multi-layer glyphs — think rich emoji and icon fonts — rendered scalably at any size.\nCOLRv0 (the original COLR format) only supported flat color fills per glyph layer. COLRv1 adds:\nLinear, radial, and sweep gradients Compositing and blending modes Variable color fonts (combining color with font variation axes) No special CSS is required — use color fonts like any other:\n1 2 3 body { font-family: \u0026#34;Noto Color Emoji\u0026#34;, sans-serif; } 1 2 3 4 5 6 7 /* Customising with font-palette */ @font-palette-values --custom { font-family: \u0026#34;Bungee Color\u0026#34;; override-colors: 0 #ff4500; } h1 { font-palette: --custom; } COLRv1 fonts are smaller than SVG or bitmap emoji fonts, render crisply at any size, support system-level accessibility color overrides, and integrate naturally with CSS forced-colors.\n","permalink":"https://www.webstf.nl/baseline/limited/#colrv1","tags":null,"title":"COLRv1"},{"categories":null,"content":"Column break properties let you control how content flows across columns in a multi-column layout — preventing awkward breaks inside headings or figures, or forcing new columns to begin at specific points.\n1 2 3 4 5 6 7 h2, h3 { break-inside: avoid; } figure { break-inside: avoid-column; } avoid prevents a break inside the element. avoid-column is more specific to multi-column contexts.\n1 2 3 .chapter-start { break-before: column; } Break Values\nValue Effect auto No forced break avoid Avoid breaking inside column Force a column break avoid-column Avoid a column break specifically page Force a page break (print) column-break-before, column-break-after, and column-break-inside are older equivalents. The current spec uses the generic break-before, break-after, and break-inside.\n","permalink":"https://www.webstf.nl/baseline/limited/#column-breaks","tags":null,"title":"Column breaks"},{"categories":null,"content":"object-view-box defines a viewport into an image or video (a \u0026ldquo;replaced element\u0026rdquo;), letting you pan and crop without JavaScript — similar to SVG\u0026rsquo;s viewBox attribute.\n1 2 3 img { object-view-box: inset(10% 20% 10% 20%); } This crops 10% from top and bottom and 20% from left and right of the image.\ninset() follows the same shorthand as margin:\n1 2 object-view-box: inset(top right bottom left); object-view-box: inset(10px 20px); /* vertical horizontal */ 1 2 3 4 5 6 img { object-fit: cover; object-view-box: inset(20% 0% 20% 0%); /* crop top and bottom */ width: 300px; height: 200px; } object-view-box is animatable, enabling Ken Burns-style pan and zoom effects in pure CSS.\n","permalink":"https://www.webstf.nl/baseline/limited/#object-view-box","tags":null,"title":"object-view-box"},{"categories":null,"content":"The reversed() function in counter-reset creates a counter that counts down from the total number of items to 1, useful for reverse-numbered lists.\n1 2 3 ol { counter-reset: reversed(list-item); } The list-item counter is the built-in counter used by ordered lists. Wrapping it in reversed() makes it count from the total down to 1.\n1 2 3 4 5 6 7 8 9 ol { counter-reset: reversed(my-counter); list-style: none; } li::before { counter-increment: my-counter; content: counter(my-counter) \u0026#34;. \u0026#34;; } HTML has a reversed attribute on \u0026lt;ol\u0026gt; that does the same thing:\n1 2 3 4 5 \u0026lt;ol reversed\u0026gt; \u0026lt;li\u0026gt;Last item\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Middle item\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;First item\u0026lt;/li\u0026gt; \u0026lt;/ol\u0026gt; The CSS reversed() function gives you the same control programmatically.\n","permalink":"https://www.webstf.nl/baseline/limited/#counter-reset-reversed","tags":null,"title":"Reversed counter-reset"},{"categories":null,"content":"Browsers now support text-decoration properties inside ::selection, letting you change or remove underlines, overlines, and strikethroughs when text is highlighted by the user.\n1 2 3 4 5 ::selection { background: #4f46e5; color: white; text-decoration: none; /* remove underlines when selected */ } Removing underlines during selection prevents them from looking awkward against the selection background color.\n1 2 3 4 5 a::selection { background: #fbbf24; color: black; text-decoration-color: black; /* match the selection text color */ } Only a limited set of properties work in ::selection:\ncolor background-color text-decoration and its longhands text-shadow stroke-color, fill-color, stroke-width (SVG) ","permalink":"https://www.webstf.nl/baseline/limited/#text-decoration-selection","tags":null,"title":"text-decoration in ::selection"},{"categories":null,"content":"The video-dynamic-range media feature is similar to dynamic-range but specifically targets the video rendering capabilities of the display, which can differ from its general display capabilities.\n1 2 3 4 5 6 7 @media (video-dynamic-range: standard) { /* Standard dynamic range video */ } @media (video-dynamic-range: high) { /* HDR video capable display */ } Some devices have separate pipelines for video and graphics rendering. A display may support HDR video (HLG, HDR10) while the graphics compositor uses standard dynamic range — or vice versa.\n1 2 3 4 5 6 7 8 9 10 11 12 13 /* General display HDR */ @media (dynamic-range: high) { } /* Video-specific HDR */ @media (video-dynamic-range: high) { } /* practical use: */ @media (video-dynamic-range: high) { video { /* Apply HDR-specific styles or hints */ color-gamut: p3; } } Adapting video player UI for HDR content Showing or hiding HDR badges on video thumbnails Providing HDR-quality poster images for video elements ","permalink":"https://www.webstf.nl/baseline/limited/#video-dynamic-range","tags":null,"title":"video-dynamic-range media query"},{"categories":null,"content":"Ruby annotations are small text displayed above or beside base text, commonly used in Japanese, Chinese, and Korean content to show pronunciation guides (furigana/bopomofo) or glosses.\n1 2 3 \u0026lt;ruby\u0026gt; 漢字 \u0026lt;rt\u0026gt;かんじ\u0026lt;/rt\u0026gt; \u0026lt;/ruby\u0026gt; The \u0026lt;rt\u0026gt; element holds the annotation. Browsers render it above the base text by default.\n1 2 3 ruby { display: ruby; } rt { display: ruby-text; } rp { display: none; } These are the default mappings, but you can apply them manually for custom elements or to restore behaviour after a reset.\n","permalink":"https://www.webstf.nl/baseline/limited/#display-ruby","tags":null,"title":"display: ruby"},{"categories":null,"content":"accent-color sets the highlight color used by checkboxes, radio buttons, range sliders, and progress elements — bringing them into your brand with a single line of CSS.\n1 2 3 :root { accent-color: #7c3aed; } Every checkbox, radio, range, and progress element on the page now uses your brand color.\n1 2 input[type=\u0026#34;checkbox\u0026#34;] { accent-color: #16a34a; } input[type=\u0026#34;range\u0026#34;] { accent-color: #dc2626; } \u0026lt;input type=\u0026quot;checkbox\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;radio\u0026quot;\u0026gt; \u0026lt;input type=\u0026quot;range\u0026quot;\u0026gt; \u0026lt;progress\u0026gt; The browser automatically adjusts the checkmark or thumb color to ensure legible contrast against your chosen accent. You don\u0026rsquo;t need to worry about white-on-light or black-on-dark issues.\nCustom form control colors previously required hiding the native element and building a replacement from scratch with custom HTML and CSS. accent-color achieves a good result in one property.\n1 2 3 4 5 6 \u0026lt;style\u0026gt; [type=radio], [type=checkbox] { accent-color: red; } \u0026lt;/style\u0026gt; checkbox radio 1 radio 2 ","permalink":"https://www.webstf.nl/baseline/limited/#accent-color","tags":null,"title":"Accent color"},{"categories":null,"content":"The ::marker pseudo-element targets the marker box of a list item — the bullet, number, or any other counter symbol — letting you style it independently from the list item\u0026rsquo;s content.\n::marker supports a limited set of CSS properties:\ncolor content font-* properties unicode-bidi and direction white-space Layout properties like margin and padding do not apply to ::marker.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \u0026lt;style\u0026gt; li::marker { content: \u0026#34;# \u0026#34;; font-size: 1.2em; font-style: italic; font-weight: bold; } \u0026lt;/style\u0026gt; \u0026lt;p\u0026gt;Frontend language\u0026lt;/p\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;Html\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Css\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Javascript\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Svg\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; Frontend language\nHtml Css Javascript Svg ","permalink":"https://www.webstf.nl/baseline/limited/#marker","tags":null,"title":"CSS ::marker"},{"categories":null,"content":"Font metric overrides — ascent-override, descent-override, line-gap-override, and size-adjust — let you tweak the metrics of a fallback font so it matches your web font\u0026rsquo;s dimensions almost exactly.\nWhen a web font loads, it often has different dimensions than the fallback font, causing text to reflow and shift layout — harming your CLS score.\nDefine a custom @font-face for your fallback that overrides its metrics to match the web font:\n1 2 3 4 5 6 7 8 9 10 11 12 @font-face { font-family: \u0026#34;FallbackForInter\u0026#34;; src: local(\u0026#34;Arial\u0026#34;); ascent-override: 90%; descent-override: 22%; line-gap-override: 0%; size-adjust: 107%; } body { font-family: \u0026#34;Inter\u0026#34;, \u0026#34;FallbackForInter\u0026#34;, sans-serif; } Tools like Font Style Matcher or Capsize can calculate the correct override percentages for any web font and fallback pair.\nThe page renders with virtually zero layout shift as the font loads, because the fallback takes up the same space as the web font.\n","permalink":"https://www.webstf.nl/baseline/limited/#font-metric-overrides","tags":null,"title":"Font metric overrides"},{"categories":null,"content":"The text-justify CSS property sets the justification method of text when text-align: justify is set.\n","permalink":"https://www.webstf.nl/baseline/limited/#text-justify","tags":null,"title":"Text justify"},{"categories":null,"content":"CSS provides generic font family keywords that map directly to the operating system\u0026rsquo;s interface fonts, letting your web app feel native on every platform.\n1 2 3 4 5 6 7 body { font-family: ui-sans-serif, system-ui, sans-serif; } code { font-family: ui-monospace, monospace; } Keyword Maps to (examples) ui-sans-serif San Francisco (macOS/iOS), Segoe UI (Windows) ui-serif New York (macOS) ui-monospace SF Mono (macOS), Consolas (Windows) ui-rounded SF Pro Rounded (iOS) Why Use System Fonts?\nZero load time — no web font download required Native feel — matches the OS the user is on Excellent readability — optimised for screens at all sizes Before these keywords, there was a long fallback stack:\n1 font-family: -apple-system, BlinkMacSystemFont, \u0026#34;Segoe UI\u0026#34;, Roboto, sans-serif; ","permalink":"https://www.webstf.nl/baseline/limited/#font-family-ui","tags":null,"title":"UI Fonts"},{"categories":null,"content":"The :active-view-transition pseudo-class matches the root element when a view transition is currently in progress. It gives you a hook to apply styles globally for the duration of any view transition, without having to target the specific pseudo-elements involved in the animation itself.\nA common use case is disabling pointer events or suppressing certain UI elements while the transition plays, preventing user interaction from interfering with the animation.\n1 2 3 4 5 6 7 8 9 :root:active-view-transition { /* Prevent clicks during the transition */ pointer-events: none; } :root:active-view-transition .sidebar { /* Hide the sidebar while a transition is running */ visibility: hidden; } :active-view-transition is a straightforward way to coordinate global state with the view transition lifecycle, complementing the more granular control offered by ::view-transition-old and ::view-transition-new.\n","permalink":"https://www.webstf.nl/baseline/2026/#active-view-transition","tags":null,"title":"active-view-transition"},{"categories":null,"content":"The rcap unit is the root-relative version of cap. Where cap measures the cap height of the current element\u0026rsquo;s font, rcap measures the cap height of the root element\u0026rsquo;s font — typically the \u0026lt;html\u0026gt; element — regardless of the font applied to the element being styled.\nThis mirrors the relationship between em and rem: rcap gives you a stable, consistent reference point that does not change as you nest elements with different font-size or font-family values.\n1 2 3 4 5 6 .icon { /* Always sized relative to the root font\u0026#39;s capital letters, not whatever font this component happens to use */ width: 1rcap; height: 1rcap; } rcap is especially useful in design systems where you want decorative elements or spacing tokens to remain anchored to a single typographic baseline across an entire page, independent of local font overrides.\n","permalink":"https://www.webstf.nl/baseline/2026/#rcap","tags":null,"title":"Rcap unit"},{"categories":null,"content":"The rch unit is the root-relative counterpart to ch. The ch unit represents the width of the \u0026ldquo;0\u0026rdquo; (zero) character in the current element\u0026rsquo;s font, making it useful for sizing text inputs or containers to a certain number of characters. rch does the same, but always references the root element\u0026rsquo;s font rather than the element\u0026rsquo;s own font.\nThis makes rch a predictable, globally stable unit for character-based measurements. When you have components that override the font family or size locally, using rch keeps any character-width-based sizing anchored to the root, avoiding unexpected layout shifts.\n1 2 3 4 5 .code-block { /* Width expressed in terms of root font character width, even if this element uses a monospace font override */ max-width: 80rch; } Like all root-relative units, rch is most valuable in systems that set a clear typographic baseline on the root element and want consistent measurements throughout the document.\n","permalink":"https://www.webstf.nl/baseline/2026/#rch","tags":null,"title":"rch"},{"categories":null,"content":"The rex unit is the root-relative version of ex. The ex unit represents the x-height of the current font — roughly the height of a lowercase \u0026ldquo;x\u0026rdquo; — and is commonly used for fine typographic adjustments. rex provides the same measurement, but always taken from the root element\u0026rsquo;s font.\nBecause x-height varies widely between typefaces, ex can produce very different results depending on what font is active on a given element. rex eliminates that variability by fixing the reference to the root font, giving you a stable unit for spacing and sizing decisions that need to feel optically connected to the base typography without being affected by local font changes.\n1 2 3 4 5 .footnote { /* Consistent vertical spacing tied to root x-height, not the footnote\u0026#39;s own smaller font */ margin-top: 1rex; } rex fits naturally alongside rem and rcap as part of a family of root-anchored typographic units, each targeting a different characteristic of the root font.\n","permalink":"https://www.webstf.nl/baseline/2026/#rex","tags":null,"title":"rex"},{"categories":null,"content":"The ric unit is the root-relative version of ic. The ic unit is similar to ch but instead of using the \u0026ldquo;0\u0026rdquo; character, it uses the \u0026ldquo;水\u0026rdquo; (CJK water ideograph) as its reference glyph, making it well-suited for sizing in CJK (Chinese, Japanese, Korean) typography where full-width characters are the norm.\nric applies the same concept but always references the root element\u0026rsquo;s font, providing a consistent baseline for CJK-aware sizing regardless of local font overrides on individual elements.\n1 2 3 4 5 .article-body { /* Maximum line length expressed in CJK character units relative to the root font */ max-width: 40ric; } For multilingual layouts where CJK and Latin text coexist, ric offers a stable typographic unit that stays anchored to a single reference point, helping keep proportions consistent across sections that may use different typefaces.\n","permalink":"https://www.webstf.nl/baseline/2026/#ric","tags":null,"title":"Ric unit"},{"categories":null,"content":"The each-line keyword extends text-indent so that indentation applies not just to the first line of a block, but to every line that begins after a forced line break (like \u0026lt;br\u0026gt; or a newline in white-space: pre).\n1 2 3 4 5 p { text-indent: 2em; } /* Only the first line is indented */ p { text-indent: 2em each-line; } /* First line AND every line after a \u0026lt;br\u0026gt; are indented */ Soft-wrapped lines (that wrap due to limited width) are NOT indented — only forced breaks.\n1 p { text-indent: 2em hanging each-line; } hanging reverses the direction (outdents the first line instead), while each-line applies the rule after forced breaks too.\nCan be used for:\nPoetry where each verse line should be indented Script or dialogue formatting with forced line breaks Legal documents with structured line-by-line indentation ","permalink":"https://www.webstf.nl/baseline/2026/#text-indent-each-line","tags":null,"title":"text-indent: each-line"},{"categories":null,"content":"The hanging keyword inverts the direction of text-indent: instead of indenting the first line, it indents every line except the first — a hanging indent (also called an outdent).\n1 2 3 4 5 6 7 8 9 10 11 /* Standard indent — first line pushed in */ p { text-indent: 2em; } /* Hanging indent — all lines except the first are pushed in */ p { text-indent: 2em hanging; } /* Classic bibliography style */ .reference { text-indent: -2em hanging; padding-left: 2em; } A negative value with hanging creates the classic bibliography format: the first line starts at the left margin, and wrapped lines are indented.\nWithout hanging: indent is positive → first line moves right. With hanging: the meaning flips → first line stays, subsequent lines move right.\nWhere to use:\nBibliographies and works-cited lists Glossaries and definition lists Dictionary entries Numbered lists with long first items 1 p { text-indent: 1.5em hanging each-line; } Combining with each-line applies the hanging indent after every forced line break as well.\n","permalink":"https://www.webstf.nl/baseline/2026/#text-indent-hanging","tags":null,"title":"text-indent: hanging"},{"categories":null,"content":"The ::details-content pseudo-element targets the expandable content area of a \u0026lt;details\u0026gt; element — everything inside it except the \u0026lt;summary\u0026gt;. This gives you direct styling control over the hidden/shown region without needing a wrapper element.\nPreviously, styling the revealed content of a \u0026lt;details\u0026gt; element required wrapping everything in a \u0026lt;div\u0026gt; and targeting that. ::details-content removes that need, and more importantly, it enables smooth animations on the open/close transition — something that was not straightforward before because the content toggled between display: none and visible with no interpolatable state in between.\n1 2 3 4 5 6 details::details-content { padding: 1rem; background: #f5f5f5; overflow: hidden; transition: height 0.3s ease, content-visibility 0.3s ease allow-discrete; } Combined with interpolate-size and transition-behavior, ::details-content makes it possible to animate the expanding and collapsing of \u0026lt;details\u0026gt; elements using only CSS, bringing native disclosure widgets in line with what previously required JavaScript.\n","permalink":"https://www.webstf.nl/baseline/2025/#details-content","tags":null,"title":"::details-content"},{"categories":null,"content":"The @scope at-rule allows you to apply styles scoped to a specific subtree of the DOM, defined by a scoping root and an optional scoping limit. Styles inside @scope only match elements within the defined scope, without the need for deeply nested selectors or BEM-style naming conventions.\n1 2 3 4 5 6 7 8 9 @scope (.card) { p { font-size: 0.9rem; } img { border-radius: 4px; } } You can also define a lower boundary to exclude nested components from the scope:\n1 2 3 4 5 @scope (.card) to (.card__footer) { a { color: var(--card-link-color); } } In this example, the a rule applies to links inside .card but not to any links inside .card__footer. This makes it straightforward to style a component without accidentally affecting nested components of the same type, solving a long-standing problem with CSS specificity and inheritance that previously required either strict naming conventions or JavaScript-based scoping solutions like CSS Modules or Shadow DOM.\nInside a @scope block, the :scope pseudo-class refers to the scoping root element itself, giving you a convenient way to style the root of the component directly.\n","permalink":"https://www.webstf.nl/baseline/2025/#scope","tags":null,"title":"@scope"},{"categories":null,"content":"abs() and sign() are CSS math functions that extend the toolkit available inside calc() and other math contexts.\nabs() returns the absolute value of its argument — always a non-negative number, regardless of whether the input is positive or negative. This is useful when you need to work with the magnitude of a value without knowing its sign in advance.\n1 2 3 4 .element { /* Ensures the offset is always positive */ margin-top: abs(var(--offset)); } sign() returns -1, 0, or 1 depending on whether its argument is negative, zero, or positive. Combined with multiplication, this lets you flip or preserve the direction of a value conditionally in pure CSS math.\n1 2 3 4 .element { /* Move left if offset is negative, right if positive */ translate: calc(sign(var(--offset)) * 10px) 0; } Both functions are particularly valuable in combination with CSS custom properties and other math functions like clamp() and round(), enabling more expressive layout logic without JavaScript.\n","permalink":"https://www.webstf.nl/baseline/2025/#abs-sign","tags":null,"title":"abs() and sign()"},{"categories":null,"content":"The content-visibility property allows the browser to skip rendering work for elements that are not currently visible in the viewport. Setting it to auto tells the browser it can defer layout, paint, and compositing for off-screen elements, resuming that work only when they are about to scroll into view.\nThis can produce significant performance improvements on long pages with many elements, as the browser does far less work on initial load and during scrolling.\n1 2 3 4 .article-card { content-visibility: auto; contain-intrinsic-size: auto 300px; } The contain-intrinsic-size property accompanies content-visibility: auto to give the browser a placeholder size for the element while its content is not rendered. Without it, all off-screen elements would collapse to zero height, causing layout jumps as they scroll into view.\ncontent-visibility: hidden is a stricter variant that keeps the element\u0026rsquo;s rendering state cached but always hidden — similar to display: none in visibility but preserving the rendered state for fast unhiding. content-visibility: visible is the default, applying no skipping behaviour.\n","permalink":"https://www.webstf.nl/baseline/2025/#content-visibility","tags":null,"title":"content-visibility"},{"categories":null,"content":"@starting-style defines the initial style state for an element before it first participates in a CSS transition. This lets you animate elements into view when they first appear in the DOM — something previously only possible with JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 .dialog { transition: opacity 0.3s, transform 0.3s; opacity: 1; transform: translateY(0); } @starting-style { .dialog { opacity: 0; transform: translateY(-20px); } } When .dialog is first inserted, it starts from the @starting-style values and transitions to its normal state.\nCan be used with \u0026lt;dialog\u0026gt; and [popover]\n1 2 3 4 5 6 7 8 9 10 dialog[open] { transition: display 0.3s allow-discrete, opacity 0.3s; opacity: 1; } @starting-style { dialog[open] { opacity: 0; } } Before @starting-style, transitions only worked when a property changed — not when an element first appeared. This fills a long-standing gap in CSS animation.\n","permalink":"https://www.webstf.nl/baseline/2024/#starting-style","tags":null,"title":"@starting-style"},{"categories":null,"content":"align-content now works in block layout contexts, not just flexbox and grid. When applied to a block-level element that has more height available than its content requires, align-content controls how that content is distributed along the block axis — vertically in horizontal writing modes.\nThis was previously only possible in block contexts by reaching for flexbox or grid just to center content vertically. Now, a regular block element can use align-content: center to vertically center its children without changing the layout model.\n1 2 3 4 5 6 7 8 9 .card { height: 300px; align-content: center; /* No need for display: flex */ } .card-footer { height: 100px; align-content: end; } Values like center, start, end, space-between, and space-around all apply. This is a meaningful simplification for common layout patterns where flexbox or grid was used purely as a centering mechanism rather than for its full layout capabilities.\n","permalink":"https://www.webstf.nl/baseline/2024/#align-content-block","tags":null,"title":"align-content-block"},{"categories":null,"content":"The content property now supports an alternative text string for accessibility purposes, using a slash separator after the content value. This allows CSS-generated content — inserted via ::before and ::after — to carry a meaningful text alternative that assistive technologies can expose, rather than reading out the raw generated string or nothing at all.\n1 2 3 4 5 6 7 8 9 10 11 .icon-home::before { content: url(\u0026#39;/icons/home.svg\u0026#39;) / \u0026#39;Home\u0026#39;; } .required-field::after { content: \u0026#39; *\u0026#39; / \u0026#39;required\u0026#39;; } .decorative::before { content: \u0026#39;❧\u0026#39; / \u0026#39;\u0026#39;; /* Empty alt = decorative, ignored by screen readers */ } The syntax is content: \u0026lt;value\u0026gt; / \u0026lt;alt-text\u0026gt;. An empty string as the alt text (/ '') signals that the generated content is purely decorative and should be ignored by screen readers, equivalent to aria-hidden. A non-empty alt text provides a spoken alternative to the visual content.\nThis removes a significant accessibility gap — previously, meaningful icons or indicators inserted via CSS had no way to communicate their purpose to assistive technology without additional HTML attributes or ARIA.\n","permalink":"https://www.webstf.nl/baseline/2024/#alt-text-generated-content","tags":null,"title":"Alt text for generated content"},{"categories":null,"content":"The backdrop-filter property applies graphical filter effects to the area behind an element, rather than to the element itself. The element must be at least partially transparent for the effect to be visible. This enables the frosted glass aesthetic — a blurred, color-tinted view of content behind a translucent panel — that is common in modern UI design.\n1 2 3 4 5 6 7 8 9 .glass-panel { background: rgb(255 255 255 / 0.2); backdrop-filter: blur(12px) saturate(180%); } .dark-overlay { background: rgb(0 0 0 / 0.3); backdrop-filter: blur(4px) brightness(0.7); } backdrop-filter accepts the same filter functions as the filter property: blur(), brightness(), contrast(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), and sepia(). Multiple functions can be chained.\nBecause the effect is composited from the live content behind the element, it updates dynamically as that content scrolls or changes. Performance varies by device and complexity of the filter; blur() in particular can be GPU-intensive at large radii or on complex backgrounds.\n","permalink":"https://www.webstf.nl/baseline/2024/#backdrop-filter","tags":null,"title":"backdrop-filter"},{"categories":null,"content":"font-size-adjust scales a font so its x-height (lowercase letter height) matches a specified ratio, keeping text visually consistent when the primary font fails to load and a fallback is used.\nDifferent fonts at the same font-size can look dramatically different sizes because they have different x-heights. A fallback font may appear much smaller or larger than the intended web font.\n1 2 3 4 body { font-family: \u0026#34;Custom Font\u0026#34;, Arial, sans-serif; font-size-adjust: 0.5; } 0.5 means the x-height should be half the font-size. The browser scales the fallback font so its x-height matches this ratio.\nThe value is the font\u0026rsquo;s aspect ratio: x-height ÷ font-size. Tools like Capsize can calculate it for any font.\n1 font-size-adjust: from-font; /* use the primary font\u0026#39;s own ratio */ The from-font keyword extracts the ratio automatically from the loaded font.\nMost valuable in long-form reading where a flash of a differently-sized fallback font is jarring. Pairs well with font metric overrides.\n","permalink":"https://www.webstf.nl/baseline/2024/#font-size-adjust","tags":null,"title":"font-size-adjust"},{"categories":null,"content":"The font-variant-emoji property controls whether emoji characters are rendered in their colorful emoji presentation or as plain text symbols. Unicode defines variation selectors that influence this (VS15 for text, VS16 for emoji), but font-variant-emoji gives you CSS-level control over the default presentation when no variation selector is present in the markup.\n1 2 3 4 5 6 7 8 9 10 11 .monochrome-icons { font-variant-emoji: text; } .colorful-labels { font-variant-emoji: emoji; } .system-default { font-variant-emoji: normal; } The text value renders the character as a monochrome text glyph, while emoji requests the full-color emoji rendering. unicode defers to the character\u0026rsquo;s default presentation as defined by Unicode, and normal leaves the decision to the browser and font.\nThis is useful when you want to use emoji characters as icon-like elements that blend with surrounding text styling, or conversely when you want to ensure colorful rendering in contexts where the text presentation might otherwise be selected.\n","permalink":"https://www.webstf.nl/baseline/2024/#font-variant-emoji","tags":null,"title":"font-variant-emoji"},{"categories":null,"content":"CSS gradients now support in \u0026lt;colorspace\u0026gt; syntax, letting you specify which color space is used when blending between gradient stops — dramatically improving the quality of color transitions.\nGradients through complementary colors in sRGB pass through a dull grey midpoint:\n1 2 /* Grey in the middle */ background: linear-gradient(to right, blue, yellow); 1 2 /* with a color-space specified */ background: linear-gradient(in oklch to right, blue, yellow); oklch produces a vivid, natural-looking transition because it interpolates through perceptually uniform hue space.\n1 2 3 4 5 6 /* Available color Spaces */ linear-gradient(in srgb, red, blue) linear-gradient(in oklch, red, blue) linear-gradient(in hsl, red, blue) linear-gradient(in lab, red, blue) linear-gradient(in display-p3, red, blue) For hue-based spaces, control which way hue rotates with the shorter, longer and increasing keywords:\n1 2 3 linear-gradient(in hsl shorter hue, red, blue) linear-gradient(in hsl longer hue, red, blue) linear-gradient(in oklch increasing hue, red, blue) ","permalink":"https://www.webstf.nl/baseline/2024/#gradient-interpolation","tags":null,"title":"Gradient interpolation"},{"categories":null,"content":"light-dark() is a CSS function that returns one of two values depending on whether the active color scheme is light or dark. It condenses what previously required a prefers-color-scheme media query into a single expression.\n1 2 color: light-dark(#333, #eee); /* Returns #333 in light mode, #eee in dark mode */ The page (or element) must declare color-scheme:\n1 2 3 :root { color-scheme: light dark; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* Before: */ :root { --text: #333; } @media (prefers-color-scheme: dark) { :root { --text: #eee; } } /* With light-dark() */ :root { color-scheme: light dark; --text: light-dark(#333, #eee); } /* Works anywhere a value is accepted */ background: light-dark(white, #1a1a2e); border-color: light-dark(#ccc, #444); box-shadow: 0 2px 8px light-dark(rgba(0,0,0,0.1), rgba(0,0,0,0.4)); ","permalink":"https://www.webstf.nl/baseline/2024/#light-dark","tags":null,"title":"light-dark()"},{"categories":null,"content":"The paint-order property controls the order in which the fill, stroke, and markers of text and SVG shapes are painted. By default, fill is painted first and stroke is painted on top of it, which means a thick stroke bleeds inward and partially covers the fill. Reversing this order lets the fill render on top of the stroke, keeping the fill clean and pushing the stroke outward.\n1 2 3 4 5 6 text { fill: white; stroke: black; stroke-width: 4px; paint-order: stroke fill; } With paint-order: stroke fill, the stroke is drawn first — acting as an outline — and the fill is rendered on top of it. This creates a crisp outlined text effect without the stroke obscuring the fill color.\npaint-order accepts fill, stroke, and markers in any order. Omitted keywords are painted after the listed ones in their default order. The property applies to both SVG elements and HTML text content in supporting browsers, making it a useful tool for accessible high-contrast text effects and decorative typographic treatments.\n","permalink":"https://www.webstf.nl/baseline/2024/#paint-order","tags":null,"title":"paint-order"},{"categories":null,"content":"Relative color syntax allows you to derive a new color from an existing one by taking it as a base and modifying individual channels. The from keyword introduces the origin color, whose channels are then exposed as named variables you can use and manipulate inside any color function.\nThis makes it straightforward to generate tints, shades, transparencies, and hue-shifted variants from a single source color — without leaving CSS, without a preprocessor, and without hardcoding intermediate values.\n1 2 3 4 5 6 7 8 9 10 .button { --brand: #3a6bc4; background: var(--brand); /* 20% transparent version of the same color */ border-color: hsl(from var(--brand) h s l / 0.2); /* Lighter variant by boosting lightness */ --brand-light: oklch(from var(--brand) calc(l + 0.15) c h); } Relative colors work across all supported color spaces and functions, including oklch, oklch, hsl, rgb, and others. Using a perceptually uniform color space like oklch typically produces the most visually consistent results when adjusting lightness or chroma. This feature effectively brings the color manipulation capabilities of Sass and design tokens into native CSS.\n","permalink":"https://www.webstf.nl/baseline/2024/#relative-color","tags":null,"title":"relative-color"},{"categories":null,"content":"These CSS math functions bring rounding and modulo operations into stylesheets, enabling snapping values to grids, cycles, and intervals.\nround(strategy, value, step) rounds a value to the nearest multiple of step:\n1 2 3 4 5 6 7 width: round(nearest, 67px, 10px); /* 70px */ width: round(up, 61px, 10px); /* 70px */ width: round(down, 69px, 10px); /* 60px */ width: round(to-zero, -15px, 10px); /* -10px */ /* Shorthand (default strategy is `nearest`) */ width: round(67px, 10px); /* 70px */ mod(value, divisor) returns the remainder with the same sign as the divisor:\n1 --step: mod(var(--n), 4); /* cycles 0→1→2→3→0... */ rem(value, divisor) returns the remainder with the same sign as the dividend (like % in most programming languages):\n1 2 --r: rem(7, 3); /* 1 */ --r: rem(-7, 3); /* -1 */ When to use:\nSnap sizes to a grid: round(var(--size), 8px) Cycle through N states with mod() Normalise angles: mod(var(--angle), 360deg) ","permalink":"https://www.webstf.nl/baseline/2024/#round-mod-rem","tags":null,"title":"round(), mod(), and rem()"},{"categories":null,"content":"The text-wrap property controls how text wraps within its container. It is a shorthand that encompasses text-wrap-mode (whether wrapping is allowed at all) and text-wrap-style (the algorithm used when wrapping does occur).\nThe most practically impactful values are balance and pretty. text-wrap: balance distributes text evenly across lines, eliminating the awkward single-word orphans that frequently appear on the last line of headings. text-wrap: pretty applies a similar improvement to body text, optimising the last few lines of a paragraph to avoid orphans — at a slightly higher computational cost than the default wrap.\n1 2 3 4 5 6 7 h1, h2, h3 { text-wrap: balance; } p { text-wrap: pretty; } text-wrap: nowrap prevents wrapping entirely, equivalent to the older white-space: nowrap. The wrap value is the default behaviour.\nThese values give you typographic control that previously required JavaScript or manual line-break insertion, and they adapt automatically as container widths change.\n","permalink":"https://www.webstf.nl/baseline/2024/#text-wrap","tags":null,"title":"text-wrap"},{"categories":null,"content":"text-wrap: balance instructs the browser to distribute text across lines as evenly as possible, eliminating the single short word left dangling on the last line of a heading.\n1 2 3 h1, h2, h3 { text-wrap: balance; } The browser adjusts where lines break so that all lines are approximately the same length.\nWithout balancing, a heading might render as:\nThe Quick Brown Fox Jumps Over With text-wrap: balance:\nThe Quick Brown Fox Jumps Over Balancing is computationally expensive for long passages of text. It is intentionally limited to short strings — browsers cap it at around 6 lines. Use it on headings and pull quotes, not body text.\nFor body text, text-wrap: pretty is the better choice — it avoids orphans (single words on the last line) without the full cost of balancing.\n","permalink":"https://www.webstf.nl/baseline/2024/#text-wrap-balance","tags":null,"title":"text-wrap: balance"},{"categories":null,"content":"transition-behavior controls whether CSS transitions apply to properties with discrete (non-interpolatable) values, such as display, visibility, and content-visibility.\nNormally, display: none cannot be transitioned — it switches instantly, so fade-out animations are cut short, but with transition-behavior: allow-discrete it can.\n1 2 3 4 5 6 7 8 9 .card { transition: opacity 0.3s, display 0.3s; transition-behavior: allow-discrete; } .card.hidden { opacity: 0; display: none; } With allow-discrete, the display: none is applied at the end of the transition, giving the opacity time to animate first.\nShorthand:\n1 transition: display 0.3s allow-discrete, opacity 0.3s; allow-discrete can be included directly in the transition shorthand.\nPair this with @starting-style for the entering animation, use @starting-style to define where the transition begins:\n1 2 3 @starting-style { .card { opacity: 0; } } ","permalink":"https://www.webstf.nl/baseline/2024/#transition-behavior","tags":null,"title":"transition-behavior"},{"categories":null,"content":"CSS now allows range inputs, progress bars, and meter elements to be oriented vertically using writing-mode or appearance, replacing non-standard attributes and workarounds.\n1 2 3 4 5 input[type=\u0026#34;range\u0026#34;] { writing-mode: vertical-lr; width: 40px; height: 200px; } This is the standard CSS approach — writing-mode: vertical-lr or vertical-rl orients the slider vertically.\n1 2 3 4 5 6 7 8 9 10 11 progress { writing-mode: vertical-lr; width: 20px; height: 150px; } meter { writing-mode: vertical-lr; width: 20px; height: 100px; } Previously, developers used the non-standard orient=\u0026quot;vertical\u0026quot; attribute on range inputs, or applied transform: rotate(90deg) which caused layout issues.\n","permalink":"https://www.webstf.nl/baseline/2024/#vertical-form-controls","tags":null,"title":"Vertical form controls"},{"categories":null,"content":"white-space-collapse is a longhand property extracted from the white-space shorthand, controlling only how whitespace characters are collapsed — independently of line wrapping behaviour.\n1 2 3 4 5 white-space-collapse: collapse; /* default — collapse sequences */ white-space-collapse: preserve; /* keep all whitespace as-is */ white-space-collapse: preserve-breaks; /* collapse spaces, keep newlines */ white-space-collapse: preserve-spaces; /* keep spaces, collapse newlines */ white-space-collapse: break-spaces; /* like preserve but wraps at spaces */ Previously you had to use white-space for everything:\n1 2 3 white-space: pre; /* preserve all + no wrapping */ white-space: pre-wrap; /* preserve all + wrap */ white-space: nowrap; /* collapse + no wrapping */ white-space-collapse pairs with text-wrap to replace the full white-space shorthand:\n1 2 3 /* Equivalent to white-space: pre-wrap */ white-space-collapse: preserve; text-wrap: wrap; Where to use:\nCode blocks: preserve for indentation and newlines Chat interfaces: preserve-breaks to honour user newlines without preserving spaces Inline labels: collapse to normalise authored whitespace ","permalink":"https://www.webstf.nl/baseline/2024/#white-space-collapse","tags":null,"title":"white-space-collapse"},{"categories":null,"content":"The @page at-rule controls the layout of printed pages — margins, size, and orientation — giving you meaningful influence over how your content looks on paper.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @page { margin: 2cm; } /* page size and orientation */ @page { size: A4 portrait; } @page landscape { size: A4 landscape; } /* Page Margin Boxes are special regions at the edges of each printed page */ @page { @top-center { content: \u0026#34;My Document\u0026#34;; } @bottom-right { content: counter(page); } } /* first and blank pages */ @page :first { margin-top: 4cm; } @page :blank { content: \u0026#34;This page intentionally left blank\u0026#34;; } ","permalink":"https://www.webstf.nl/baseline/2024/#page-setup","tags":null,"title":"Page setup"},{"categories":null,"content":"CSS now supports named mathematical constants inside calc() and other math functions: e, pi, infinity, -infinity, and NaN. These bring expressive mathematical notation directly into CSS without any JavaScript.\n1 2 width: calc(pi * 100px); /* ~314.16px */ height: calc(e * 1rem); /* ~2.72rem */ pi — Pi (~3.14159)\nUseful for circular geometry calculations:\n1 2 3 4 5 .dial { --radius: 80px; --circumference: calc(2 * pi * var(--radius)); /* ~502.65px */ stroke-dasharray: var(--circumference); } e — Euler\u0026rsquo;s Number (~2.71828)\nUseful for exponential scaling curves:\n1 2 3 .step-3 { font-size: calc(pow(e, 0.5) * 1rem); /* ~1.65rem */ } infinity and -infinity\n1 2 z-index: calc(infinity); /* effectively the maximum z-index */ width: calc(infinity); /* resolves to the largest possible value */ NaN (Not a Number) resolves to 0 when used as a length. It is mostly useful as an intermediate value in complex math expressions where an invalid result needs to be handled gracefully.\nThese constants compose naturally with pow(), sqrt(), sin(), and cos():\n1 2 width: calc(sqrt(pow(3, 2) + pow(4, 2)) * 1px); /* 5px — Pythagorean theorem */ height: calc(sin(pi / 6) * 200px); /* 100px — sin(30°) */ ","permalink":"https://www.webstf.nl/baseline/2023/#calc-constants","tags":null,"title":"calc() keywords"},{"categories":null,"content":"The :dir() pseudo-class matches elements based on their text directionality — left-to-right or right-to-left — as determined by the document or the dir attribute.\n1 2 :dir(ltr) { /* left-to-right elements */ } :dir(rtl) { /* right-to-left elements */ } Basic Example\n1 2 3 4 5 6 7 8 9 10 11 :dir(rtl) .icon { transform: scaleX(-1); /* flip arrow icons for RTL */ } :dir(ltr) blockquote { border-left: 4px solid #4f46e5; } :dir(rtl) blockquote { border-right: 4px solid #4f46e5; } :dir() uses the computed directionality from the HTML dir attribute or Unicode bidirectional algorithm — not just a CSS property. This makes it more semantically robust than [dir=\u0026quot;rtl\u0026quot;].\nDifference from [dir] Attribute Selector\n1 2 [dir=\u0026#34;rtl\u0026#34;] { } /* matches only elements with dir=\u0026#34;rtl\u0026#34; set explicitly */ :dir(rtl) { } /* matches all RTL elements, including inherited direction */ When to use\nFlipping directional icons (arrows, carets) Swapping border sides for blockquotes and list indents Adjusting text alignment in bidirectional documents ","permalink":"https://www.webstf.nl/baseline/2023/#dir-pseudo","tags":null,"title":":dir()"},{"categories":null,"content":"The of \u0026lt;selector\u0026gt; syntax for :nth-child() lets you count only elements that match a given selector, rather than counting all siblings and then filtering.\n1 2 3 4 5 /* Selects even li elements (counts ALL children) */ li:nth-child(even) { } /* Selects even li.featured elements (counts only .featured) */ li:nth-child(even of .featured) { } Without of, :nth-child(2) picks the second child regardless of its type or class. With of .featured, it picks the second element that has the .featured class.\n1 2 3 4 5 6 7 8 9 10 /* Alternate background on visible cards only, ignoring hidden ones */ .card:not(.hidden):nth-child(odd of :not(.hidden)) { background: #f9fafb; } /* Syntax */ :nth-child(An+B of selector) :nth-child(2 of .highlight) :nth-child(odd of li) :nth-child(3n+1 of .card) ","permalink":"https://www.webstf.nl/baseline/2023/#nth-child-of","tags":null,"title":":nth-child() of"},{"categories":null,"content":"The cap unit represents the cap height of the current font — that is, the approximate height of uppercase letters. This is distinct from the em unit, which is based on the full em square of the font, and from ex, which approximates the x-height of lowercase letters.\nUsing cap lets you size elements in relation to the visual height of capital letters rather than the theoretical em square. This makes it particularly useful when you want spacing or sizing to feel optically aligned with uppercase text in a way that em alone cannot guarantee, since cap height varies significantly between typefaces.\n1 2 3 4 5 h1 { /* An icon sized to match the capital letters beside it */ width: 1cap; height: 1cap; } Because cap is a font-relative unit, it responds automatically to changes in font-size and font-family, making it a reliable choice for designs where elements need to stay in proportion with the uppercase letterforms around them.\nButton text ","permalink":"https://www.webstf.nl/baseline/2023/#cap","tags":null,"title":"Cap unit"},{"categories":null,"content":"The color-gamut media feature detects the approximate color gamut of the user\u0026rsquo;s display, letting you serve enhanced wide-gamut colors to capable screens.\n1 2 3 4 5 6 7 8 9 10 11 @media (color-gamut: srgb) { /* Standard color range — all modern displays */ } @media (color-gamut: p3) { /* Display P3 gamut — most modern phones and laptops */ } @media (color-gamut: rec2020) { /* Very wide gamut — high-end HDR monitors */ } Values are inclusive — a p3 display also matches srgb.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 :root { --brand: #0055ff; } @media (color-gamut: p3) { :root { --brand: color(display-p3 0.1 0.3 1.0); } } @media (color-gamut: rec2020) { :root { --brand: color(rec2020 0.05 0.2 0.95); } } /* Combining with `dynamic-range` */ @media (color-gamut: p3) and (dynamic-range: high) { /* Full HDR P3 treatment */ } Use for\nVivid brand colors on capable displays Enhanced photography and illustration rendering Wide-gamut gradient backgrounds ","permalink":"https://www.webstf.nl/baseline/2023/#color-gamut","tags":null,"title":"color-gamut media query"},{"categories":null,"content":"color-mix() mixes two colors together in a specified color space, producing a new color. It is the CSS equivalent of blending paint.\n1 color: color-mix(in oklch, #ff0000 40%, #0000ff 60%); Arguments: color space, first color + percentage, second color + percentage.\n1 2 3 4 5 6 7 8 9 10 11 color: color-mix(in srgb, blue, white); /* Percentages default to 50% each */ :root { --brand: #4f46e5; } .light { background: color-mix(in oklch, var(--brand) 30%, white); } .dark { background: color-mix(in oklch, var(--brand) 70%, black); } .muted { background: color-mix(in oklch, var(--brand) 50%, gray); } /* the colorspace makes a difference */ color-mix(in srgb, red, blue) /* dull purple */ color-mix(in oklch, red, blue) /* vivid violet */ oklch and lab produce more perceptually natural results.\n1 color-mix(in srgb, rgb(255 0 0 / 0.5), blue) Alpha values are also mixed proportionally.\ncolor-mix(in oklch, var(--brand) 30%, white) color-mix(in srgb, rgb(255 0 0 / 0.5), blue) ","permalink":"https://www.webstf.nl/baseline/2023/#color-mix","tags":null,"title":"color-mix()"},{"categories":null,"content":"Container queries let you apply styles based on the size of a parent element rather than the viewport. A component can now respond to its own available space, making it truly reusable across any layout.\n1 2 3 4 5 6 7 8 9 10 11 12 13 /* setting up a container */ .sidebar { container-type: inline-size; container-name: sidebar; } /* and using the container in a query */ @container sidebar (width \u0026gt;= 300px) { .widget { display: flex; gap: 1rem; } } When .sidebar is at least 300px wide, widgets switch to a flex layout — regardless of the viewport width.\n1 2 3 4 5 6 /* container don\u0026#39;t have to be named - anonymous containers */ .wrapper { container-type: inline-size; } @container (width \u0026gt;= 400px) { .card { flex-direction: row; } } Container Units\nInside a container query, use cqi, cqb, cqw, cqh units relative to the container size:\n1 2 3 @container (width \u0026gt;= 400px) { h2 { font-size: clamp(1rem, 5cqi, 2rem); } } ","permalink":"https://www.webstf.nl/baseline/2023/#container-queries","tags":null,"title":"Container queries"},{"categories":null,"content":"The CSS Typed Object Model (Typed OM) is a JavaScript API that exposes CSS property values as typed objects rather than plain strings, making programmatic CSS manipulation faster, safer, and easier.\n1 2 3 4 5 6 7 8 9 10 11 12 /* reading values */ const el = document.querySelector(\u0026#39;.box\u0026#39;); const styles = el.computedStyleMap(); const width = styles.get(\u0026#39;width\u0026#39;); console.log(width.value); // 200 console.log(width.unit); // \u0026#34;px\u0026#34; /* writing values */ el.attributeStyleMap.set(\u0026#39;width\u0026#39;, CSS.px(300)); el.attributeStyleMap.set(\u0026#39;opacity\u0026#39;, CSS.number(0.5)); el.attributeStyleMap.set(\u0026#39;transform\u0026#39;, new CSSTranslate(CSS.px(10), CSS.px(20))); CSS Factory Functions\n1 2 3 4 CSS.px(10) // CSSUnitValue { value: 10, unit: \u0026#39;px\u0026#39; } CSS.percent(50) // CSSUnitValue { value: 50, unit: \u0026#39;percent\u0026#39; } CSS.deg(45) // CSSUnitValue { value: 45, unit: \u0026#39;deg\u0026#39; } CSS.number(1.5) // CSSUnitValue { value: 1.5, unit: \u0026#39;number\u0026#39; } Why this is better than getComputedStyle\nNo string parsing — values come as typed objects with .value and .unit Math is straightforward: width.value + 10 instead of parseFloat(width) + 10 Errors are caught early with meaningful messages ","permalink":"https://www.webstf.nl/baseline/2023/#css-typed-om","tags":null,"title":"CSS typed object model"},{"categories":null,"content":"Animate To and From display: none\nCSS can now animate elements to and from display: none using transition-behavior: allow-discrete combined with @starting-style, enabling true enter and exit animations without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .dialog { /* Exit transition */ transition: display 0.3s allow-discrete, opacity 0.3s, transform 0.3s; opacity: 1; transform: scale(1); display: block; } .dialog.hidden { opacity: 0; transform: scale(0.95); display: none; } /* Enter starting state */ @starting-style { .dialog { opacity: 0; transform: scale(0.95); } } How does this work\nEntering — @starting-style defines where the transition begins when the element first renders Exiting — allow-discrete delays display: none until the transition completes When used on \u0026lt;dialog\u0026gt; and Popovers\n1 2 3 4 5 6 dialog[open] { transition: display 0.3s allow-discrete, opacity 0.3s; opacity: 1; } dialog:not([open]) { opacity: 0; display: none; } @starting-style { dialog[open] { opacity: 0; } } ","permalink":"https://www.webstf.nl/baseline/2023/#display-animation","tags":null,"title":"display animation"},{"categories":null,"content":"font-synthesis-small-caps controls whether the browser synthesises small-caps glyphs by scaling down regular capitals when the font has no true small-caps variant.\nFaux small caps are regular capitals scaled down ~75%. They look thinner and lighter than the surrounding text because they are not designed at that size — unlike true small-caps, which are drawn with appropriate stroke weights.\n1 2 3 :root { font-synthesis-small-caps: none; } With synthesis disabled, font-variant-small-caps has no effect on fonts without true small caps — preserving typographic integrity.\n1 2 3 4 5 font-synthesis-small-caps: auto; /* shorthand */ font-synthesis: none; /* disable all synthesis */ font-synthesis: small-caps; /* allow only small-caps synthesis */ Pair font-synthesis-small-caps: none with a font that includes genuine small-caps, or avoid using font-variant-small-caps on fonts that lack them.\n","permalink":"https://www.webstf.nl/baseline/2023/#font-synthesis-small-caps","tags":null,"title":"font-synthesis-small-caps"},{"categories":null,"content":"font-synthesis-style controls whether the browser synthesises italic by algorithmically slanting the roman (upright) glyphs when the font has no true italic variant.\nSynthesised italic is a geometric shear of the regular glyphs. It lacks the curves, alternate letterforms, and optical corrections of a properly drawn italic, producing a poor typographic result.\n1 2 3 :root { font-synthesis-style: none; } When disabled, font-style: italic renders as upright text if no italic font file is available.\n1 font-synthesis-style: auto; If you load only the regular weight of a variable or web font, disable style synthesis to prevent \u0026lt;em\u0026gt; and \u0026lt;i\u0026gt; elements from rendering a faux slant:\n1 2 3 4 5 6 7 body { font-synthesis-style: none; } em, i { font-style: normal; color: #4f46e5; } /* use color instead */ /* shorthand */ font-synthesis: none; /* disable all synthesis including style */ font-synthesis: style; /* allow only style synthesis */ ","permalink":"https://www.webstf.nl/baseline/2023/#font-synthesis-style","tags":null,"title":"font-synthesis-style"},{"categories":null,"content":"font-synthesis-weight controls whether the browser synthesises bold by artificially thickening glyphs when the font has no true bold variant available.\nSynthesised bold adds uniform stroke weight to the regular font. The result looks uneven — spacing between letters is not adjusted, and the glyphs lack the design refinements of a true bold cut.\n1 2 3 :root { font-synthesis-weight: none; } With synthesis disabled, font-weight: bold on a font without a bold variant renders at normal weight rather than a faux bold.\n1 font-synthesis-weight: auto; font-synthesis-weight is a longhand of font-synthesis:\n1 2 font-synthesis: none; /* disable all: weight, style, small-caps */ font-synthesis: weight; /* allow only weight synthesis */ Always load the bold font file if your design uses bold text. Use font-synthesis-weight: none to catch cases where the bold variant has not loaded — it will fail visibly rather than silently substituting a degraded faux version.\n","permalink":"https://www.webstf.nl/baseline/2023/#font-synthesis-weight","tags":null,"title":"font-synthesis-weight"},{"categories":null,"content":"The font-variant-alternates property enables access to alternate glyph forms defined in OpenType fonts — stylistic sets, swashes, ornaments, annotations, and character variants. These are additional glyphs the type designer has included as alternatives to the standard letterforms.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @font-feature-values \u0026#39;MyFont\u0026#39; { @styleset { flowing: 1; elegant: 2; } @character-variant { alt-a: 1; alt-g: 2; } @swash { fancy: 1; } } .styleset { font-variant-alternates: styleset(flowing); } .swash-caps { font-variant-alternates: swash(fancy); } .ornamental { font-variant-alternates: ornaments(1); } .annotated { font-variant-alternates: annotation(1); } The @font-feature-values at-rule maps human-readable names to the numeric feature indices defined in the font, making the code more readable than using raw font-feature-settings values.\nThe functional values accepted by font-variant-alternates include stylistic() (a single alternate glyph), styleset() (a complete alternate style set), character-variant() (specific character alternates), swash() (decorative letterforms), ornaments() (decorative non-letter glyphs), and annotation() (enclosed or circled numerals and letters).\nThe availability of these features depends entirely on the font — not all fonts include alternate glyphs. Tools like the browser\u0026rsquo;s font inspector or applications like Wakamai Fondue help identify which OpenType features a specific font supports.\n","permalink":"https://www.webstf.nl/baseline/2023/#font-variant-alternates","tags":null,"title":"font-variant-alternates"},{"categories":null,"content":"hyphenate-character sets the character displayed at the end of a hyphenated line when hyphens: auto breaks a word. By default, the hyphen-minus (-) is used.\n1 2 3 4 p { hyphens: auto; hyphenate-character: \u0026#34;‐\u0026#34;; /* proper typographic hyphen U+2010 */ } The Unicode hyphen character (‐, U+2010) is typographically correct for hyphenation — distinct from the hyphen-minus (-) used in code and arithmetic.\n1 2 3 4 p { hyphenate-character: \u0026#34;–\u0026#34;; } /* en dash */ p { hyphenate-character: \u0026#34;=\u0026#34;; } /* equals sign — unusual but valid */ hyphenate-character: auto; /* browser/language default */ Lets the browser or language setting choose the most appropriate character.\nIn professional publishing contexts — print stylesheets, ebooks, editorial web typography — the distinction between - and ‐ is typographically meaningful. Most readers will never notice, but the difference is there.\n","permalink":"https://www.webstf.nl/baseline/2023/#hyphenate-character","tags":null,"title":"Hyphenate character"},{"categories":null,"content":"The lh unit equals the computed line-height of the element it is used on. It lets you size and space things in terms of text lines, making vertical rhythm much simpler to express.\n1 2 3 4 5 6 7 8 9 10 .pull-quote { margin-block: 1lh; /* one line of space above and below */ padding-inline: 0.5lh; } .icon { width: 1lh; height: 1lh; /* icon sized to match line height */ vertical-align: middle; } Before lh, achieving one line of spacing meant calculating 1.5rem (or whatever your line height happened to be), then updating it manually if the line height changed. 1lh always stays in sync.\nDifference from em and rem\n1em = font-size of the current element 1lh = line-height of the current element (typically ~1.5× the font-size) 1rlh = line-height of the root element Vertical Rhythm\n1 2 p + p { margin-top: 1lh; } h2 { margin-top: 2lh; } ","permalink":"https://www.webstf.nl/baseline/2023/#lh","tags":null,"title":"lh unit"},{"categories":null,"content":"The linear() easing function lets you define a custom animation timing curve by providing a list of progress points. It is the foundation for recreating spring physics, bounces, and complex custom easings in pure CSS.\n1 animation-timing-function: linear(0, 0.5, 1); Values are output progress (0–1) sampled at evenly spaced input intervals. Three values means: at 0% input → 0 output, at 50% → 0.5, at 100% → 1.\n1 2 3 4 5 6 animation-timing-function: linear( 0, 0.004, 0.016, 0.035, 0.063, 0.099, 0.143 13.6%, 0.25, 0.39, 0.56, 0.76, 1, 0.86, 0.77, 0.73, 0.75, 0.83, 0.91, 0.97, 1 78.2%, 0.96, 1 ); You can specify the input position explicitly for more control:\n1 2 linear(0, 0.25 50%, 1) /* at 50% of the animation duration, progress is 25% */ Tools like the linear() generator let you draw a curve visually and export the linear() function.\n","permalink":"https://www.webstf.nl/baseline/2023/#linear-easing","tags":null,"title":"linear() easing"},{"categories":null,"content":"The range syntax for media queries uses standard comparison operators (\u0026lt;, \u0026gt;, \u0026lt;=, \u0026gt;=) instead of min- and max- prefixes, making breakpoints cleaner and more intuitive.\n1 2 3 4 5 /* Old syntax */ @media (min-width: 600px) and (max-width: 1200px) { } /* New syntax */ @media (600px \u0026lt;= width \u0026lt;= 1200px) { } 1 2 3 4 5 6 7 @media (width \u0026gt;= 768px) { } /* was: min-width: 768px */ @media (width \u0026lt; 480px) { } /* was: max-width: 479px */ @media (height \u0026gt; 600px) { } @media (resolution \u0026gt;= 2dppx) { } /* Mixed conditions */ @media (width \u0026gt;= 600px) and (prefers-color-scheme: dark) { } The old min-width/max-width naming is counterintuitive — \u0026ldquo;min-width: 600px\u0026rdquo; means \u0026ldquo;when the viewport is at least 600px\u0026rdquo;. The range syntax reads as plain mathematics.\nIt also removes the off-by-one edge case: max-width: 599px and min-width: 600px left exactly 1px ambiguity. \u0026lt; 600px and \u0026gt;= 600px are unambiguous.\n","permalink":"https://www.webstf.nl/baseline/2023/#media-query-range-syntax","tags":null,"title":"Media query range syntax"},{"categories":null,"content":"CSS now supports native nesting — writing selectors inside other selectors — without any preprocessor like Sass or Less. Nested rules inherit the context of their parent.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .card { padding: 1rem; border-radius: 8px; .title { font-size: 1.25rem; font-weight: bold; } p { color: #555; } } 1 2 3 4 5 6 7 8 9 10 11 12 /* Parent Selector with `\u0026amp;` */ .button { background: #4f46e5; \u0026amp;:hover { background: #4338ca; } \u0026amp;.large { padding: 1rem 2rem; } } \u0026amp; refers to the outer selector.\nMedia Queries Inside Rules\n1 2 3 4 5 6 7 .hero { font-size: 2rem; @media (width \u0026lt; 600px) { font-size: 1.25rem; } } Native nesting reduces repetition, keeps related styles co-located, and makes stylesheets much easier to scan and maintain — previously requiring a build step.\n","permalink":"https://www.webstf.nl/baseline/2023/#nesting","tags":null,"title":"Nesting"},{"categories":null,"content":"oklab() and oklch() are improved perceptually uniform color spaces that fix known issues with the older Lab and LCH spaces — producing more predictable palettes, gradients, and color mixing.\n1 color: oklab(0.63 -0.1 0.1); L (0–1), a (green–red), b (blue–yellow). Range is 0–1 unlike Lab\u0026rsquo;s 0–100.\n1 color: oklch(65% 0.2 140); L (lightness), C (chroma), H (hue 0–360). The most intuitive of the perceptual spaces.\nBecause regular LCH produces unwanted hue shifts — especially blues turning purple at high chroma, OkLCh was added to correct this, giving true hue-stable chroma variation.\n1 2 3 4 5 6 7 8 9 10 11 /* color palette */ :root { --blue-light: oklch(80% 0.12 250); --blue-mid: oklch(55% 0.20 250); --blue-dark: oklch(35% 0.18 250); } /* Changing only `L` gives true tints and shades with a consistent hue. */ /* in gradients */ background: linear-gradient(in oklch, blue, yellow); /* Produces a vivid, natural transition without the grey midpoint of sRGB. */ linear-gradient(in oklch to right, blue, yellow) ","permalink":"https://www.webstf.nl/baseline/2023/#oklab","tags":null,"title":"Oklab and OkLCh"},{"categories":null,"content":"The overflow-block and overflow-inline media features detect how the device handles content that overflows the viewport — whether it scrolls, paginates, or clips it entirely.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @media (overflow-block: scroll) { /* Normal scrolling screen — most browsers */ } @media (overflow-block: paged) { /* Paginated output — printers, ebooks */ } @media (overflow-block: optional-paged) { /* Can paginate or scroll — presentation slides */ } @media (overflow-block: none) { /* Content is clipped — no scrolling possible */ } 1 2 3 4 5 6 7 8 9 10 11 12 @media (overflow-inline: scroll) { /* horizontal scroll possible */ } @media (overflow-inline: none) { /* horizontal overflow is clipped */ } /* Disable sticky headers for paged media */ @media (overflow-block: paged) { header { position: static; } } /* Simplify layout for non-scrolling displays */ @media (overflow-block: none) { .sidebar { display: none; } } Mostly used for print stylesheets that behave differently from screen.\n","permalink":"https://www.webstf.nl/baseline/2023/#overflow","tags":null,"title":"Overflow media queries"},{"categories":null,"content":"CSS now includes pow(), sqrt(), hypot(), log(), and exp() for use inside calc() and other math contexts, enabling expressive mathematical layouts without JavaScript.\npow(base, exponent)\n1 font-size: calc(pow(1.25, 3) * 1rem); /* 1.953rem — modular scale */ sqrt(value)\n1 width: calc(sqrt(2) * 100px); /* ~141px — diagonal of a square */ hypot(a, b, ...) — Euclidean Distance\n1 2 /* Hypotenuse of a 3×4 right triangle = 5 */ gap: calc(hypot(3, 4) * 1rem); /* 5rem */ log(value, base?) and exp(value)\n1 2 --level: 3; font-size: calc(exp(var(--level) * 0.2) * 1rem); log() defaults to natural log (base e). exp(x) returns eˣ.\nPossible usage:\n1 2 3 4 :root { --ratio: 1.25; } h1 { font-size: calc(pow(var(--ratio), 4) * 1rem); } h2 { font-size: calc(pow(var(--ratio), 3) * 1rem); } h3 { font-size: calc(pow(var(--ratio), 2) * 1rem); } ","permalink":"https://www.webstf.nl/baseline/2023/#exp-functions","tags":null,"title":"pow(), sqrt(), hypot(), log(), and exp()"},{"categories":null,"content":"The rlh unit equals the computed line-height of the root element (\u0026lt;html\u0026gt;). It is the root-relative counterpart to lh, useful for consistent global vertical rhythm.\n1 2 3 4 5 6 7 8 9 10 :root { font-size: 1rem; line-height: 1.6; /* 1rlh = 1.6rem globally */ } section { margin-block: 2rlh; /* two global line heights */ padding: 1rlh; } lh vs rlh\nlh = line-height of the current element (changes with font-size overrides) rlh = line-height of the root element (constant across the page) 1 2 3 4 5 6 7 .small-text { font-size: 0.75rem; line-height: 1.4; /* 1lh = 0.75rem × 1.4 = 1.05rem */ /* 1rlh = still the root line-height */ margin-top: 1rlh; /* consistent with page rhythm */ } rlh is the natural companion to rem for typographically grounded layout: rem for font-size-relative sizing, rlh for line-height-relative spacing.\n","permalink":"https://www.webstf.nl/baseline/2023/#rlh","tags":null,"title":"Rlh unit"},{"categories":null,"content":"The scripting media query detects whether JavaScript is available and enabled in the user\u0026rsquo;s browser, letting you provide meaningful CSS fallbacks for no-JS environments.\n1 2 3 4 5 6 7 8 9 10 11 @media (scripting: enabled) { /* JavaScript is available */ } @media (scripting: initial-only) { /* JS ran during page load but is now disabled (e.g. printed page) */ } @media (scripting: none) { /* No JavaScript available */ } 1 2 3 4 5 6 7 8 9 /* Hide JS-powered component, show CSS fallback */ @media (scripting: none) { .js-accordion { display: none; } .no-js-details { display: block; } } @media (scripting: enabled) { .no-js-details { display: none; } } Rather than relying on adding a js class to \u0026lt;html\u0026gt; with JavaScript, scripting lets CSS handle this detection natively.\nUse for:\nShowing static content when a JS-powered widget would otherwise be empty Adjusting navigation for no-JS users Print stylesheets where JS-triggered states may not apply ","permalink":"https://www.webstf.nl/baseline/2023/#scripting","tags":null,"title":"scripting media query"},{"categories":null,"content":"CSS now supports sin(), cos(), tan(), asin(), acos(), atan(), and atan2() — enabling geometric calculations directly in CSS without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .box { width: calc(100px * cos(30deg)); height: calc(100px * sin(30deg)); } /* Placing Items on a Circle */ .item { --angle: 60deg; --r: 150px; transform: translateX(calc(var(--r) * cos(var(--angle)))) translateY(calc(var(--r) * sin(var(--angle)))); } /* Inverse Functions */ --angle: atan2(4, 3); /* angle whose tangent is 4/3 ≈ 53.13deg */ atan2(y, x) takes two arguments for the correct quadrant handling.\nFunctions that accept an angle work with deg, rad, turn, and grad. Inverse functions return values in rad — convert with / 1rad * 1deg if needed.\n","permalink":"https://www.webstf.nl/baseline/2023/#trig-functions","tags":null,"title":"sin(), cos(), tan(), asin(), acos(), atan(), and atan2()"},{"categories":null,"content":"subgrid lets a child grid participate in its parent\u0026rsquo;s track sizing, so columns and rows automatically align across nested components without any duplication of grid definitions.\nThe problem without subgrid is that if the parent changes, the child must be manually updated.\n1 2 .parent { display: grid; grid-template-columns: 1fr 2fr 1fr; } .child { display: grid; grid-template-columns: 1fr 2fr 1fr; } /* repeated! */ With subgrid:\n1 2 3 4 5 6 7 8 9 10 .parent { display: grid; grid-template-columns: 1fr 2fr 1fr; } .child { grid-column: 1 / -1; /* span all parent columns */ display: grid; grid-template-columns: subgrid; } The child\u0026rsquo;s columns mirror the parent\u0026rsquo;s exactly — always.\nsubgrid can be uses on both axes\n1 2 3 4 5 6 7 .child { grid-column: 1 / -1; grid-row: 1 / 3; display: grid; grid-template-columns: subgrid; grid-template-rows: subgrid; } Best used for card grids:\n1 2 .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); } .card { display: grid; grid-template-rows: subgrid; grid-row: span 3; } Card images, titles, and footers align across all cards automatically.\n","permalink":"https://www.webstf.nl/baseline/2023/#subgrid","tags":null,"title":"Subgrid"},{"categories":null,"content":"The update media feature describes how quickly the output device can refresh its display — letting you disable animations for e-ink screens and print output that cannot handle them.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @media (update: fast) { /* Normal screen — animations are fine */ } @media (update: slow) { /* E-ink or slow-refresh display */ } @media (update: none) { /* Printed output — no visual updates */ } /* Disabling animations for E-Ink */ .spinner { animation: spin 1s linear infinite; } @media (update: slow), (update: none) { .spinner { animation: none; } } update is related to prefers-reduced-motion\nupdate — hardware capability (can the screen even refresh?) prefers-reduced-motion — user preference (do they want reduced motion?) Check both for the most accessible experience:\n1 2 3 @media (update: fast) and (prefers-reduced-motion: no-preference) { /* Full animations */ } ","permalink":"https://www.webstf.nl/baseline/2023/#update","tags":null,"title":"Update frequency media query"},{"categories":null,"content":":user-valid and :user-invalid are like :valid and :invalid — but they only apply after the user has interacted with the field, preventing error styles from showing on page load before the user has typed anything.\nThe problem with :invalid is that users see errors before they\u0026rsquo;ve done anything.\n1 2 /* Shows red border on empty required fields immediately on page load */ input:invalid { border-color: red; } These styles only activate after the user has interacted with and then left the field.\n1 2 3 4 5 6 7 8 input:user-invalid { border-color: #dc2626; outline: 2px solid #dc2626; } input:user-valid { border-color: #16a34a; } When they trigger\n:user-invalid — after the user edits the field and it fails validation :user-valid — after the user edits the field and it passes validation ","permalink":"https://www.webstf.nl/baseline/2023/#user-pseudos","tags":null,"title":":user-valid and :user-invalid"},{"categories":null,"content":"The color() function lets you specify colors in color spaces beyond standard sRGB — including display-p3, a98-rgb, prophoto-rgb, and more — unlocking richer, more vivid colors on capable displays.\n1 2 3 .vivid { color: color(display-p3 0.2 0.9 0.4); } Values are in the range 0–1 for each channel (red, green, blue in display-p3).\nSupported color spaces:\nsrgb — standard web colors display-p3 — wider gamut, ~25% more colors than sRGB a98-rgb — Adobe RGB, used in print workflows rec2020 — very wide gamut for HDR video xyz, xyz-d50, xyz-d65 — device-independent 1 2 3 4 5 6 7 8 9 10 /* Progressive enhancement */ .button { background: #00cc55; /* sRGB fallback */ background: color(display-p3 0.1 0.8 0.35); /* P3 if supported */ } /* Or use `@supports`: */ @supports (color: color(display-p3 0 0 0)) { .button { background: color(display-p3 0.1 0.8 0.35); } } ","permalink":"https://www.webstf.nl/baseline/2023/#color-function","tags":null,"title":"color()"},{"categories":null,"content":"The @counter-style at-rule lets you define entirely custom counter styles for lists, going far beyond the built-in disc, decimal, or lower-roman options.\n1 2 3 4 5 6 7 8 9 @counter-style thumbs { system: cyclic; symbols: \u0026#34;\\1F44D\u0026#34;; suffix: \u0026#34; \u0026#34;; } ul { list-style: thumbs; } Each list item now gets a 👍 as its marker.\nsystem — how symbols are cycled (cyclic, numeric, alphabetic, additive) symbols — the characters or images used as markers suffix — what follows the marker (default is \u0026quot;. \u0026quot;) prefix — what precedes the marker range — which counter values this style applies to 1 2 3 4 @counter-style bold-decimal { system: extends decimal; suffix: \u0026#34;) \u0026#34;; } extends lets you tweak an existing style rather than building from scratch.\nUse it for:\nEmoji bullet points Custom legal or academic numbering (1.1, 1.2…) Language-specific list conventions ","permalink":"https://www.webstf.nl/baseline/2023/#counter-style","tags":null,"title":"CSS @counter-style"},{"categories":null,"content":"image-set() is the CSS equivalent of the HTML srcset attribute — it lets you provide multiple versions of a background image at different resolutions, and the browser picks the most appropriate one.\n1 2 3 4 5 6 7 .hero { background-image: image-set( url(\u0026#34;hero.webp\u0026#34;) type(\u0026#34;image/webp\u0026#34;), url(\u0026#34;hero.jpg\u0026#34;) 1x, url(\u0026#34;hero@2x.jpg\u0026#34;) 2x ); } On a high-DPI screen, the browser uses hero@2x.jpg. On a standard screen, it uses hero.jpg.\nResolution descriptors:\n1 2 3 4 5 background-image: image-set( \u0026#34;low-res.png\u0026#34; 1x, \u0026#34;hi-res.png\u0026#34; 2x, \u0026#34;ultra.png\u0026#34; 3x ); The type() function lets you provide modern formats with fallbacks:\n1 2 3 4 5 image-set( url(\u0026#34;image.avif\u0026#34;) type(\u0026#34;image/avif\u0026#34;), url(\u0026#34;image.webp\u0026#34;) type(\u0026#34;image/webp\u0026#34;), url(\u0026#34;image.jpg\u0026#34;) 1x ) Include -webkit-image-set() for wider compatibility alongside the standard version.\n","permalink":"https://www.webstf.nl/baseline/2023/#image-set","tags":null,"title":"image-set()"},{"categories":null,"content":"CSS lab() and lch() express colors in perceptually uniform color spaces — meaning equal numerical steps produce equal-looking differences in color, which sRGB does not guarantee.\n1 2 3 4 5 6 7 8 9 .element { color: lab(50% -40 60); /* Three values: `L` (lightness 0–100), `a` (green–red axis), `b` (blue–yellow axis). */ } .element { color: lch(60% 80 120); /* Three values: `L` (lightness), `C` (chroma/saturation), `H` (hue angle 0–360). LCH is often more intuitive than Lab because hue is a simple angle. */ } Creating color palettes in sRGB can produce steps that look visually uneven — some jumps appear larger than others. Lab and LCH produce palettes that look evenly spaced to the human eye.\noklch() is a newer, improved version of LCH with better perceptual uniformity. Many design systems are adopting it for design tokens.\n1 color: oklch(65% 0.2 140); ","permalink":"https://www.webstf.nl/baseline/2023/#lab","tags":null,"title":"Lab and LCH"},{"categories":null,"content":"CSS now supports a two-value syntax for display that separates the outer display type (how the element participates in layout) from the inner display type (how it lays out its children).\n1 2 3 4 5 6 .box { display: block flow; /* same as: block */ display: inline flex; /* same as: inline-flex */ display: block grid; /* same as: grid */ display: inline flow-root; /* same as: inline-block */ } The traditional single-value keywords (block, inline-flex, grid) are shorthand for combined inner and outer values. The two-value syntax makes this explicit and more logical.\nCommon mappings\nLegacy Two-value block block flow inline inline flow inline-block inline flow-root flex block flex inline-flex inline flex grid block grid Most developers still use the legacy single-value keywords — they are shorter and widely understood. The two-value syntax is more useful for understanding how display works conceptually and when teaching CSS.\n","permalink":"https://www.webstf.nl/baseline/2023/#two-value-display","tags":null,"title":"Two-value display property"},{"categories":null,"content":"When you use CSS containment (content-visibility: auto), the browser skips rendering off-screen elements. But without a size hint, it treats them as zero-sized, causing layout jumps. contain-intrinsic-size solves this.\ncontain-intrinsic-size tells the browser how large a contained element probably is before it has been rendered.\n1 2 3 4 .card { content-visibility: auto; contain-intrinsic-size: 0 200px; } Here, the browser reserves 200px of height for each .card even while it is off-screen.\nUsing auto as a value lets the browser remember the last known rendered size and use it as the hint on subsequent renders — the best of both worlds.\n1 contain-intrinsic-size: auto 200px; Prevents large scroll position jumps Improves Cumulative Layout Shift (CLS) scores Pairs perfectly with content-visibility: auto for long pages ","permalink":"https://www.webstf.nl/baseline/2023/#contain-intrinsic-size","tags":null,"title":"CSS contain-intrinsic-size"},{"categories":null,"content":"The counter-set property allows you to assign a specific value to a CSS counter, making it easy to control numbering in lists, chapters, or any sequenced content.\nUnlike counter-reset (which always resets to zero or a default) and counter-increment (which adds to a counter), counter-set lets you jump directly to any number you choose.\n1 2 3 .chapter { counter-set: section 5; } This sets the section counter to 5 immediately, so the next increment will produce 6.\nCombine counter-set with counter-increment and content in ::before pseudo-elements for fully custom, CSS-driven numbering systems without touching your HTML markup.\n","permalink":"https://www.webstf.nl/baseline/2023/#counter-set","tags":null,"title":"CSS counter-set"},{"categories":null,"content":"The hyphens property controls whether the browser automatically inserts hyphens when breaking long words across lines. It is a simple but effective way to improve text flow in narrow columns.\n1 2 3 p { hyphens: none; } /* Never hyphenate */ p { hyphens: manual; } /* Only at \u0026amp;shy; soft hyphens */ p { hyphens: auto; } /* Browser hyphenates automatically */ 1 2 3 4 p { hyphens: auto; -webkit-hyphens: auto; } The lang attribute on your HTML element is required for auto to work correctly, as the browser uses language-specific hyphenation dictionaries.\n1 \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; Even with hyphens: manual, you can place soft hyphens in HTML to suggest break points:\n1 \u0026lt;p\u0026gt;This is a very long­word that may need breaking.\u0026lt;/p\u0026gt; The soft hyphen (\u0026amp;shy;) is invisible unless the line breaks there.\nIt\u0026rsquo;s best for justified text, narrow sidebars, and responsive layouts where long words cause overflow.\n","permalink":"https://www.webstf.nl/baseline/2023/#hyphens","tags":null,"title":"Hyphenation"},{"categories":null,"content":"The long awaited \u0026lsquo;parent\u0026rsquo; selector :has() finally landed in 2023 in all major browsers.\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;strong\u0026gt;Lorem ipsum dolor sit amet,\u0026lt;/strong\u0026gt; \u0026lt;p\u0026gt;consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .box { aspect-ratio: 1; background-color: oklch(from rebeccapurple calc(l + 0.5) c h / 70%); max-width: 240px; width: 48%; padding: 0.5em; \u0026amp;:has(strong) { background-color: oklch(from rebeccapurple calc(l - 0.25) c h / 70%); display: flex; flex-direction: column; gap: 0.5em; } } \u0026lt;/style\u0026gt; ","permalink":"https://www.webstf.nl/baseline/2023/#has","tags":null,"title":"Css :has()"},{"categories":null,"content":"CSS 3D transforms extend the 2D transform system into the third dimension, allowing elements to be rotated, translated, and scaled along the z-axis. The parent element must establish a 3D rendering context using transform-style: preserve-3d for children to occupy genuine 3D space relative to each other.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .scene { perspective: 800px; /* Distance from viewer to z=0 plane */ } .card { transform-style: preserve-3d; transition: transform 0.6s ease; } .card:hover { transform: rotateY(180deg); } .card-front, .card-back { backface-visibility: hidden; /* Hide the rear face when rotated away */ } .card-back { transform: rotateY(180deg); /* Pre-rotated to face away initially */ } The key 3D transform functions are rotateX(), rotateY(), rotateZ(), rotate3d(), translateZ(), translate3d(), scaleZ(), scale3d(), and perspective(). translateZ() moves an element toward or away from the viewer — positive values bring it closer, negative values push it further away.\nperspective on a parent element sets the vanishing point depth for all 3D-transformed children. A lower value produces a more dramatic perspective effect; a higher value is subtler. perspective-origin controls the position of the vanishing point, defaulting to the centre of the element.\nbackface-visibility: hidden prevents the back face of a transformed element from showing through when it is rotated more than 90 degrees away from the viewer — essential for card-flip effects.\n","permalink":"https://www.webstf.nl/baseline/2022/#transforms3d","tags":null,"title":"3D transforms"},{"categories":null,"content":"Cascade layers give you explicit control over the order in which groups of styles are evaluated in the cascade, independently of specificity. Declared with the @layer at-rule, each layer sits in a defined position in the cascade stack, and styles in a higher layer always win over styles in a lower layer — regardless of selector specificity.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @layer base, components, utilities; @layer base { a { color: #0066cc; } } @layer components { .button { color: white; background: #0066cc; } } @layer utilities { .text-red { color: red; } } Declaring the layer order upfront with @layer base, components, utilities; establishes the stack before any rules are added, making the intended priority explicit. Styles in utilities will always override those in components, which will always override base — regardless of which selectors are used inside each layer.\nStyles declared outside any layer sit above all layers in the cascade, which means unlayered styles take highest priority. This is important when integrating third-party stylesheets: wrapping them in a layer pushes them below your own unlayered styles, preventing specificity conflicts.\n1 2 /* Third-party styles wrapped in a layer — your styles will override them */ @import url(\u0026#39;third-party.css\u0026#39;) layer(vendor); Layers can also be nested:\n1 2 3 4 5 6 7 @layer components { @layer forms { input { border: 1px solid #ccc; } } } Nested layers are referenced with dot notation: components.forms. This allows large codebases to organise styles hierarchically without losing control over cascade order.\nCascade layers are a fundamental tool for managing style architecture at scale, reducing the specificity arms race that commonly arises in large projects and making it practical to integrate external styles without !important overrides.\n","permalink":"https://www.webstf.nl/baseline/2022/#cascade-layers","tags":null,"title":"Cascade Layers"},{"categories":null,"content":"The contain property allows you to indicate that an element and its subtree are independent from the rest of the document, enabling the browser to limit the scope of layout, style, and paint recalculations to that subtree. This can significantly improve rendering performance in complex pages.\n1 2 3 4 5 6 7 8 9 10 11 .card { contain: layout style paint; } .widget { contain: strict; /* Equivalent to layout style paint size */ } .article { contain: content; /* Equivalent to layout style paint */ } The individual containment types can be combined or applied through shorthand keywords:\nlayout containment isolates the element from the outside layout — changes inside cannot affect elements outside, and vice versa. style containment limits the scope of CSS counters and quotes. paint containment ensures the element\u0026rsquo;s contents do not visually overflow its box. size containment means the element\u0026rsquo;s size does not depend on its children.\nstrict enables all four types simultaneously. content enables layout, style, and paint — the most commonly useful combination, without size containment which requires an explicit size to be set.\n1 2 3 4 .isolated-component { contain: content; /* The browser knows changes here cannot affect the rest of the page */ } Containment is also the foundation that container queries build on — container-type: inline-size implicitly applies layout and style containment to enable querying. content-visibility: auto uses containment internally to skip rendering off-screen elements.\n","permalink":"https://www.webstf.nl/baseline/2022/#contain","tags":null,"title":"contain"},{"categories":null,"content":"The font-synthesis property controls whether the browser is allowed to synthesise bold, italic, small-caps, and subscript/superscript font faces when the requested variant is not available in the font family. Synthesis produces an approximation by algorithmically transforming the regular face — thickening strokes for bold, slanting characters for italic — which is generally considered lower quality than a true designed variant.\n1 2 3 4 5 6 7 8 9 10 11 .no-fake-bold { font-synthesis: none; /* Disable all synthesis */ } .allow-italic-only { font-synthesis: style; /* Allow italic synthesis only */ } body { font-synthesis: weight style; /* Default: allow both */ } The font-synthesis shorthand accepts a space-separated list of the synthesis types to permit: weight (bold), style (italic/oblique), small-caps, and position (sub/super). Setting font-synthesis: none disables all synthesis; listing specific keywords enables only those.\nDisabling synthesis is particularly important when a font family includes only a regular face — if bold is applied without synthesis, the text simply renders at normal weight rather than a fake-bold approximation. This is often preferable for display typefaces where the synthesised result is visually poor.\nIndividual longhands — font-synthesis-weight, font-synthesis-style, font-synthesis-small-caps, and font-synthesis-position — each accept auto (allow synthesis) or none (disallow it).\n","permalink":"https://www.webstf.nl/baseline/2022/#font-synthesis","tags":null,"title":"font-synthesis"},{"categories":null,"content":"Layout containment, established with contain: layout, isolates the layout of an element\u0026rsquo;s subtree from the rest of the page. The browser can skip recalculating layout for the contained subtree when changes occur outside it, and vice versa — changes inside cannot trigger layout recalculations outside the boundary.\n1 2 3 .card-grid .card { contain: layout; } Layout containment establishes several formatting context boundaries automatically: the element becomes a block formatting context (similar to overflow: hidden), a new stacking context, and a new absolute positioning containing block. This means:\nFloats inside the element cannot affect content outside it Margins do not collapse across the containment boundary Absolutely positioned children are contained within the element rather than escaping to a positioned ancestor 1 2 3 4 5 6 .widget { contain: layout; /* Absolutely positioned children are scoped here */ /* Floats are contained here */ /* No margin collapse with siblings */ } Layout containment is particularly valuable for components that are rendered many times on a page — such as cards, list items, or table rows. When one card\u0026rsquo;s content changes, the browser only needs to recalculate layout within that card\u0026rsquo;s containment boundary, rather than the entire page.\nIt is one of the containment types included in both contain: content and contain: strict.\n","permalink":"https://www.webstf.nl/baseline/2022/#contain-layout","tags":null,"title":"Layout containment"},{"categories":null,"content":"Paint containment, established with contain: paint, ensures that an element\u0026rsquo;s descendants do not paint outside its border box. The browser can skip painting the subtree entirely when it is off-screen, and can optimise repaints so that only the contained element\u0026rsquo;s area is repainted when its content changes.\n1 2 3 .card { contain: paint; } Paint containment has a similar visual effect to overflow: hidden — content that would overflow the element\u0026rsquo;s box is clipped — but it also provides the performance benefit of containing the paint area. When a card\u0026rsquo;s content changes, the browser only repaints the pixels within that card\u0026rsquo;s box rather than potentially invalidating a larger region of the page.\nLike layout containment, paint containment establishes a new stacking context and block formatting context, and acts as an absolute positioning containing block for descendants.\n1 2 3 4 5 .animation-container { contain: paint; /* Animations inside are clipped to this box */ /* Only this area is repainted during animations */ } Paint containment is included in contain: content and contain: strict. It is particularly effective for elements containing animations or frequently changing content, as it limits how much of the page the browser needs to repaint. Combined with layout containment, it forms the content shorthand — the most commonly useful containment combination.\n","permalink":"https://www.webstf.nl/baseline/2022/#contain-paint","tags":null,"title":"Paint containment"},{"categories":null,"content":"The resolution media feature queries the pixel density of the output device, allowing different styles or assets to be served to standard and high-resolution displays. It accepts values in dppx (dots per pixel), dpi (dots per inch), or dpcm (dots per centimetre).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /* Standard resolution */ .logo { background-image: url(\u0026#39;logo.png\u0026#39;); } /* High-resolution displays (retina, HiDPI) */ @media (resolution \u0026gt;= 2dppx) { .logo { background-image: url(\u0026#39;logo@2x.png\u0026#39;); background-size: contain; } } @media (resolution \u0026gt;= 3dppx) { .logo { background-image: url(\u0026#39;logo@3x.png\u0026#39;); background-size: contain; } } 1dppx corresponds to a standard display where one CSS pixel equals one physical pixel. 2dppx matches retina and HiDPI screens where two physical pixels render each CSS pixel, producing sharper images. The dpi unit can also be used — 96dpi is equivalent to 1dppx and 192dpi to 2dppx.\nThe modern range syntax makes the queries readable: (resolution \u0026gt;= 2dppx) is cleaner than the older (min-resolution: 2dppx). Both forms are supported.\nFor most modern use cases, serving vector SVG assets eliminates the need for resolution queries entirely — SVGs scale perfectly at any pixel density. Resolution queries remain relevant for raster images (PNG, JPEG, WebP) where separate files at different resolutions are needed for optimal sharpness.\n","permalink":"https://www.webstf.nl/baseline/2022/#resolution","tags":null,"title":"resolution media query"},{"categories":null,"content":"Size containment, established with contain: size, means that the element\u0026rsquo;s size is not influenced by its children. The browser can calculate the element\u0026rsquo;s size without looking at its content, which allows layout calculations to be skipped for the subtree when sizing the element.\n1 2 3 4 5 6 .fixed-card { contain: size; width: 300px; height: 200px; /* Size is independent of content — children don\u0026#39;t affect these dimensions */ } Without an explicit size set, size containment causes the element to collapse to zero in both dimensions — because if the element\u0026rsquo;s size cannot depend on its children, and no size is specified, there is nothing to size it to. This makes size containment almost always require an explicit width and height (or block-size and inline-size) to be useful.\nSize containment can be applied to just one axis with contain: inline-size (the inline axis only) or contain: block-size (the block axis only), giving finer control:\n1 2 3 4 .container-query-host { container-type: inline-size; /* container-type implicitly applies inline-size containment */ } Size containment is included in contain: strict but not contain: content, as it requires an explicit size which is not always desirable. It is the containment type implicitly applied by container-type: inline-size when setting up container queries, which is its most common real-world application.\n","permalink":"https://www.webstf.nl/baseline/2022/#contain-size","tags":null,"title":"Size containment"},{"categories":null,"content":"Style containment, established with contain: style, limits the scope of CSS properties that can affect elements outside a containment boundary. Specifically, it scopes CSS counters and quotes properties so that counter values and quote nesting levels do not bleed out of the contained subtree into the rest of the document.\n1 2 3 4 5 6 7 8 9 10 .article { contain: style; counter-reset: section; } .article h2::before { counter-increment: section; content: counter(section) \u0026#39;. \u0026#39;; } /* Counter increments here don\u0026#39;t affect counters outside .article */ Without style containment, CSS counters on one component can accidentally affect counter values in sibling components if they share the same counter name. Style containment prevents this by treating each contained element as a new scope for counter inheritance and the open-quote/close-quote nesting level.\nStyle containment does not affect most CSS properties — it does not isolate layout, painting, or size. It is narrowly focused on the two areas (counters and quotes) where CSS can have unintentional global side effects from within a component.\nIn practice, style containment is most often applied as part of contain: content or contain: strict rather than on its own, since the broader containment types that include it also bring more significant performance benefits.\n","permalink":"https://www.webstf.nl/baseline/2022/#contain-style","tags":null,"title":"Style containment"},{"categories":null,"content":"The text-align-last property controls the alignment of the last line of a block of text, or of a line that ends with a forced line break. It is particularly useful when text-align: justify is set, since justification does not apply to the last line by default — that line is left-aligned and can appear awkwardly short in some layouts.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .justified { text-align: justify; text-align-last: justify; /* Also justify the last line */ } .centred-last { text-align: left; text-align-last: center; /* Centre only the final line */ } .end-aligned { text-align: justify; text-align-last: end; } The accepted values are the same as text-align: auto, left, right, center, start, end, and justify. The default auto applies the same alignment as text-align to the last line, which for justified text means the last line inherits the fallback alignment (typically left).\ntext-align-last applies to each line that immediately precedes a forced break — including lines before \u0026lt;br\u0026gt; elements — not just the very last line of the whole block. In practice it is most commonly used in combination with text-align: justify for typographic control over fully-justified text blocks.\n","permalink":"https://www.webstf.nl/baseline/2022/#text-align-last","tags":null,"title":"text-align-last"},{"categories":null,"content":"The text-combine-upright property compresses a sequence of characters into the space of a single character, rotating them upright within a vertical text run. This is used for the typographic feature known as \u0026ldquo;tate-chuu-yoko\u0026rdquo; in Japanese — the insertion of horizontal numerals or abbreviations within vertical text so they read naturally without breaking the vertical flow.\n1 2 3 4 5 6 7 .year { writing-mode: vertical-rl; } .year span { text-combine-upright: all; /* Compress the year number upright */ } 1 2 \u0026lt;p class=\u0026#34;year\u0026#34;\u0026gt;令和\u0026lt;span\u0026gt;6\u0026lt;/span\u0026gt;年\u0026lt;/p\u0026gt; \u0026lt;!-- The \u0026#34;6\u0026#34; appears upright and compressed within the vertical text --\u0026gt; Without text-combine-upright, numbers in vertical Japanese text would either be rotated sideways or stack vertically one digit at a time. With it, short sequences of characters (typically two to four digits) are fitted into a single em square, reading horizontally while the surrounding text flows vertically.\nThe all value compresses all characters in the element. The digits value (with an optional integer for the maximum number of digits) compresses only numeric digit sequences of up to that many digits — for example, text-combine-upright: digits 2 compresses sequences of one or two digits.\nThis property has no visual effect in horizontal writing modes — it only applies when the element is in a vertical text context established by writing-mode.\n","permalink":"https://www.webstf.nl/baseline/2022/#text-combine-upright","tags":null,"title":"text-combine-upright"},{"categories":null,"content":"hwb() expresses colors as a hue angle plus amounts of white and black mixed in. Many designers find it more intuitive than HSL for creating tints and shades.\n1 color: hwb(200 20% 10%); 200 — hue angle (same as in HSL, 0–360) 20% — whiteness (how much white is mixed in) 10% — blackness (how much black is mixed in) Creating tints and shades:\n1 2 3 4 5 6 7 8 9 10 11 /* Base color */ color: hwb(200 0% 0%); /* Tint — add white */ color: hwb(200 40% 0%); /* Shade — add black */ color: hwb(200 0% 40%); /* Grey — whiteness + blackness \u0026gt;= 100% */ color: hwb(200 50% 50%); HSL uses saturation and lightness, which interact in less obvious ways. HWB\u0026rsquo;s whiteness and blackness are independent additive values that feel more like mixing paint.\nWith Alpha:\n1 color: hwb(200 20% 10% / 0.5); ","permalink":"https://www.webstf.nl/baseline/2022/#hwb","tags":null,"title":"HWB"},{"categories":null,"content":"The ::backdrop pseudo-element is a box the size of the viewport, which is rendered immediately beneath any element being presented in the top layer, like a dialog or popover.\nShow popover Popover heading Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 \u0026lt;button popovertarget=\u0026#34;example-pop\u0026#34; popovertargetaction=\u0026#34;show\u0026#34;\u0026gt; Show popover \u0026lt;/button\u0026gt; \u0026lt;div class=\u0026#34;popover\u0026#34; id=\u0026#34;example-pop\u0026#34; popover\u0026gt; \u0026lt;h2\u0026gt;Popover heading\u0026lt;/h2\u0026gt; \u0026lt;p\u0026gt; Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; button { width: fit-content; border: 1px solid; border-radius: 3px; padding: 0.5em; } .popover { top: 50%; left: 50%; translate: -50% -50%; padding: 1rem; border-radius: 4px; } .popover::backdrop { backdrop-filter: blur(3px); } \u0026lt;/style\u0026gt; ","permalink":"https://www.webstf.nl/baseline/2022/#backdrop","tags":null,"title":"::backdrop"},{"categories":null,"content":"This allows the animation of rows and columns in a grid layout.\nUse grid-template-rows to animate from 0 to auto and in combination with a details summary it is possible to show and hide content without javascript.\nClick to see... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 \u0026lt;div class=\u0026#34;example-wrapper\u0026#34;\u0026gt; \u0026lt;details\u0026gt; \u0026lt;summary\u0026gt;Click to see...\u0026lt;/summary\u0026gt; \u0026lt;/details\u0026gt; \u0026lt;div class=\u0026#34;detail-content\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;inner\u0026#34;\u0026gt; Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .example-wrapper { border: 1px solid; border-radius: 4px; padding: 1rem; margin-block: 1rem; max-width: 60ch; details { cursor: pointer; } .detail-content { display: grid; grid-template-rows: 0fr; /* yes, it has to be 0fr */ transition: grid-template-rows 0.5s ease-out; } .inner { overflow: hidden; /* no parts will be sticking out */ } details[open] + .detail-content { grid-template-rows: 1fr; } } \u0026lt;/style\u0026gt; ","permalink":"https://www.webstf.nl/baseline/2022/#grid-animation","tags":null,"title":"Grid Animation"},{"categories":null,"content":"The :focus-visible pseudo-class in CSS applies styles to an element when it is focused, but only if the user needs to see a focus indicator, such as when using a keyboard.\n1 2 3 4 5 6 7 8 9 \u0026lt;style\u0026gt; .next-button:focus { outline: none; } .next-button:focus-visible { outline: 3px solid rgb(from var(--theme-main-text-color) r g b / 0.33); } \u0026lt;/style\u0026gt; \u0026lt;button class=\u0026#34;next-button\u0026#34;\u0026gt;Use \u0026#39;tab\u0026#39; to focus this button\u0026lt;/button\u0026gt; Use 'tab' to focus this button ","permalink":"https://www.webstf.nl/baseline/2022/#focus-visible","tags":null,"title":"Focus visible"},{"categories":null,"content":"overflow: clip clips overflowing content at the element\u0026rsquo;s edge but, unlike overflow: hidden, it does not create a new block formatting context or a scroll container.\n1 2 3 4 5 /* Creates a scroll container — affects stacking and positioning */ .box { overflow: hidden; } /* Clips content only — no side effects */ .box { overflow: clip; } This matters because overflow: hidden can trap position: sticky children, while overflow: clip does not. You can clip only horizontally or only vertically:\n1 2 3 4 .horizontal-clip { overflow-x: clip; overflow-y: visible; } This is impossible with hidden — setting one axis to hidden forces the other to auto.\nUse Cases\nClipping decorative background elements that bleed out Preventing horizontal scroll without breaking sticky headers Isolating overflow in card components cleanly ","permalink":"https://www.webstf.nl/baseline/2022/#overflow-clip","tags":null,"title":"Overflow Clip"},{"categories":null,"content":"prefers-contrast: takes the a11y contrast user settings in account. Various operating systems support such preferences.\n1 2 3 4 5 \u0026lt;style\u0026gt; @media (prefers-contrast: more) { outline-width: 2px; } \u0026lt;/style\u0026gt; ","permalink":"https://www.webstf.nl/baseline/2022/#prefers-contrast","tags":null,"title":"Prefers-contrast media query"},{"categories":null,"content":"The dynamic-range media feature lets your CSS detect whether the user\u0026rsquo;s screen supports standard or high dynamic range, enabling you to tailor colors and contrast for HDR displays.\n1 2 3 4 5 6 7 8 9 10 11 @media (dynamic-range: high) { :root { --brand-color: color(display-p3 0.2 0.6 1.0); } } @media (dynamic-range: standard) { :root { --brand-color: #3399ff; } } HDR users get a wide-gamut display-p3 color; standard users get a regular sRGB color.\nstandard — a typical display with standard dynamic range high — a display with HDR and wide color gamut (OLED or HDR monitors) 1 2 3 4 /* Pairing with color-gamut */ @media (dynamic-range: high) and (color-gamut: p3) { /* Serve premium visuals */ } Richer hero images and color palettes on HDR screens Enhanced gradients that exceed sRGB gamut Logo colors that pop on capable displays ","permalink":"https://www.webstf.nl/baseline/2022/#dynamic-range","tags":null,"title":"Dynamic-range Media Query"},{"categories":null,"content":"Appearance is mostly used to remove vendor styling from form controls, like:\n1 2 3 select { appearance: none; } Make a choice Option 1 Option 2 Option 3 ","permalink":"https://www.webstf.nl/baseline/2022/#appearance","tags":null,"title":"Appearance"},{"categories":null,"content":"CSS Motion Path — primarily the offset-path property — allows you to move elements along a defined geometric path using CSS animations. No JavaScript required.\n1 2 3 4 5 6 7 8 9 .ball { offset-path: path(\u0026#34;M 0 0 C 100 -100 200 100 300 0\u0026#34;); offset-distance: 0%; animation: move 2s linear infinite; } @keyframes move { to { offset-distance: 100%; } } The element travels from 0% to 100% along the SVG path curve.\nPath Types\npath() — SVG path data string ray() — a straight line at a given angle circle(), ellipse(), polygon() — CSS shape functions Auto Rotation\n1 2 3 4 .car { offset-path: path(\u0026#34;M 0 0 L 300 100 L 600 0\u0026#34;); offset-rotate: auto; } offset-rotate: auto makes the element face its direction of travel automatically.\n","permalink":"https://www.webstf.nl/baseline/2022/#motion-path","tags":null,"title":"CSS Motion Path"},{"categories":null,"content":"The translate, rotate, and scale CSS properties apply single transformations independently, as opposed to applying multiple transformations with the transform CSS property.\nWhat does that mean?\nIn supporting browsers you can use translate, scale and rotate without transform: they\u0026rsquo;re their own properties now.\n1 2 3 4 5 6 7 8 9 .old-transform { transform: translate(100px, 10px) rotate(35deg) scale(1.5); } .new-properties { translate: 100px 10px; rotate: 35deg; scale: 1.5; } ","permalink":"https://www.webstf.nl/baseline/2022/#individual-transforms","tags":null,"title":"Individual transform properties"},{"categories":null,"content":"see: forced-colors ","permalink":"https://www.webstf.nl/baseline/2022/#forced-colors","tags":null,"title":"Forced colors"},{"categories":null,"content":"These units are relative to the smallest, largest, and current (dynamic) viewport size.\ndvh is dynamic viewport height\ndvw is dynamic viewport width\nsvh is smallest viewport height\nsvw is smallest viewport width\nlvh is largest viewport height\nlvw is largest viewport width\nA good use case for dvh is fullscreen sheets on mobile for menu\u0026rsquo;s or shopping carts\n1 2 3 4 .sheet { height: 100dvh; ... } ","permalink":"https://www.webstf.nl/baseline/2022/#viewport-unit-variants","tags":null,"title":"Viewport Unit Variants"},{"categories":null,"content":"The fit-content keyword (and fit-content() function) sizes an element to fit its content, up to the available space in the container. It behaves like max-content — growing to wrap content without breaking — but is capped at the available width, after which it wraps like a normal block.\n1 2 3 4 5 6 7 8 9 10 11 .tag { width: fit-content; /* Shrinks to content, won\u0026#39;t overflow container */ padding: 0.25rem 0.75rem; border-radius: 999px; } .tooltip { width: fit-content; max-width: 20rem; white-space: normal; } In grid layout, fit-content() as a track sizing function accepts a clamp argument — the maximum size the track will grow to, even if the content is larger:\n1 2 3 .grid { grid-template-columns: fit-content(200px) 1fr fit-content(300px); } This creates columns that size to their content up to the given maximum, then stop growing — the remaining space goes to the 1fr column.\nAs a width or height value, fit-content is equivalent to min(max-content, max(min-content, available)) — it takes as much space as the content needs, but never more than the available space, and never less than the minimum content size. This makes it useful for buttons, badges, tooltips, and any element that should hug its content without overflowing.\n","permalink":"https://www.webstf.nl/baseline/2021/#fit-content","tags":null,"title":"fit-content"},{"categories":null,"content":"The each-line keyword for text-indent applies the indentation not just to the first line, but to every line that follows a forced line break — giving you more control over indented text blocks.\nBy default, text-indent only affects the very first line of a block:\n1 2 3 4 5 6 7 8 9 10 11 p { text-indent: 2em; } /* with each-line */ p { text-indent: 2em each-line; } p { text-indent: 2em hanging each-line; } Now every line that begins after a \u0026lt;br\u0026gt; or a forced break is also indented. Lines that wrap naturally (soft wraps) are not affected.\nBoth keywords can be used together — hanging reverses the indent direction (outdent the first line) while each-line applies it after forced breaks too.\nUse cases:\nPoetry formatting where each verse line should be indented Legal or script formatting with explicit line breaks Custom typographic layouts Ik ben de blauwbilgorgel. Ik ben de blauwbilgorgel,\nMijn vader was een porgel,\nMijn moeder was een porulan,\nDaar komen vreemde kind'ren van.\nRaban! Raban! Raban!\nIk ben een blauwbilgorgel\nIk lust alleen maar korgel,\nBehalve als de nachtuil krijst,\nDan eet ik riep en rimmelrijst.\nRabijst! Rabijst! Rabijst!\nIk ben een blauwbilgorgel,\nAls ik niet wok of worgel,\nDan lig ik languit in de zon\nEn knoester met mijn knezidon.\nRabon! Rabon! Rabon!\nIk ben een blauwbilgorgel\nEens sterf ik aan de schorgel,\nEn schrompel als een kriks ineen\nEn word een blauwe kiezelsteen.\nGa heen! Ga heen! Ga heen!\nAuthor: Cees Buddingh' ","permalink":"https://www.webstf.nl/baseline/2021/#text-indent-each-line","tags":null,"title":"text-indent: each-line"},{"categories":null,"content":"The hanging keyword inverts the direction of text-indent — instead of indenting the first line, it indents every line except the first. This is also known as a hanging indent or outdent.\n1 2 3 4 5 /* Standard: first line indented */ p { text-indent: 2em; } /* Hanging: all lines except the first */ p { text-indent: 2em hanging; } Hanging indents are the standard format for bibliography entries:\nA negative value combined with hanging creates a classic bibliography style where the first line starts at the left edge and subsequent lines are indented.\n1 2 3 4 5 6 7 8 9 .reference { text-indent: -2em hanging; padding-left: 2em; } p { /* Applies the hanging indent after every forced line break as well.*/ text-indent: 1.5em hanging each-line; } Use cases:\nBibliography and works-cited lists Dictionary or glossary entries Definition lists in custom typographic styles ","permalink":"https://www.webstf.nl/baseline/2021/#text-indent-hanging","tags":null,"title":"text-indent: hanging"},{"categories":null,"content":":where() takes a selector list as its argument, and matches any element that can be selected by one of the selectors in that list, without increasing specificity.\n1 2 3 4 5 6 /* Base reset — zero specificity, always overridable */ :where(h1, h2, h3, h4, h5, h6) { font-weight: 700; line-height: 1.2; margin-top: 0; } :where() works exactly like :is() — it matches elements that satisfy any selector in its list — but it always contributes zero specificity. This makes it perfect for default and base styles.\n1 2 :is(header, main) h2 { color: navy; } /* specificity: 0,0,1 */ :where(header, main) h2 { color: navy; } /* specificity: 0,0,0 */ With :where(), any user override with a simple h2 selector will win.\nLike :is() , :where() uses forgiving parsing — invalid selectors are silently ignored.\n","permalink":"https://www.webstf.nl/baseline/2021/#where","tags":null,"title":":where()"},{"categories":null,"content":":not() matches every element that does not match the selector inside it. Since CSS Selectors Level 4, it accepts a full selector list, making exclusions much more expressive.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* Style all paragraphs that don\u0026#39;t have .intro */ p:not(.intro) { color: #444; } /* Exclude multiple classes at once */ li:not(.active, .disabled) { opacity: 0.6; } /* Add dividers between items, not after the last one */ .menu-item:not(:last-child) { border-bottom: 1px solid #eee; } :not() itself adds no specificity — but its argument does. :not(.active) adds class-level specificity (0,1,0).\n","permalink":"https://www.webstf.nl/baseline/2021/#not","tags":null,"title":":not()"},{"categories":null,"content":":user-valid and :user-invalid\n","permalink":"https://www.webstf.nl/baseline/2021/#user-pseudos","tags":null,"title":":user-valid and :user-invalid"},{"categories":null,"content":"quotes The default value of quotes is auto. The browser provides language specific quotation marks when the \u0026lt;q\u0026gt; element is used.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;style\u0026gt; .custom { quotes: \u0026#39;--\u0026#39; \u0026#39;--\u0026#39;; } \u0026lt;/style\u0026gt; \u0026lt;div lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;q\u0026gt;This is an English quote.\u0026lt;/q\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div lang=\u0026#34;nl\u0026#34;\u0026gt; \u0026lt;q\u0026gt;Dit is een Nederlandse quote.\u0026lt;/q\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;q class=\u0026#34;custom\u0026#34;\u0026gt;Dit is een Nederlandse quote.\u0026lt;/q\u0026gt; \u0026lt;/div\u0026gt; This is an English quote. Dit is een Nederlandse quote. This is a custom quote. ","permalink":"https://www.webstf.nl/baseline/2021/#quotes","tags":null,"title":"Quotes"},{"categories":null,"content":"tab-size defines the width of the tab character (\\t) when it appears in rendered text. It only affects white-space: pre or white-space: pre-wrap contexts, like \u0026lt;pre\u0026gt; and \u0026lt;code\u0026gt; elements.\n1 2 3 pre { tab-size: 2; } The browser default is 8 spaces. Setting it to 2 or 4 better matches common code style guides.\nInstead of a number (in space characters), you can use a length:\n1 2 3 pre { tab-size: 1.5rem; } Without tab-size, actual tab characters render as huge 8-space gaps. Setting tab-size: 4 makes code look as intended.\n","permalink":"https://www.webstf.nl/baseline/2021/#tab-size","tags":null,"title":"Tab size"},{"categories":null,"content":":is() accepts a list of selectors and matches any element that matches at least one of them. It is primarily a readability and maintenance tool.\n1 2 3 4 5 6 /* before :is() */ header h1, header h2, header h3, main h1, main h2, main h3, footer h1, footer h2, footer h3 { color: #333; } 1 2 3 4 /* width :is() */ :is(header, main, footer) :is(h1, h2, h3) { color: #333; } Much cleaner with identical results.:is() takes the specificity of its most specific argument. So :is(#id, p) has ID-level specificity even when matching a \u0026lt;p\u0026gt;.\n`:is()` uses forgiving parsing — an invalid selector in the list is ignored rather than invalidating the entire rule.","permalink":"https://www.webstf.nl/baseline/2021/#is","tags":null,"title":":is()"},{"categories":null,"content":"The very useful gap from grid layout gap column-gap row-gap is now also available for flex containers as gap.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 \u0026lt;div class=\u0026#34;parent\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;box\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;box\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;box\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .box { background: #00fb29; padding: 0.5rem 1rem; } .parent { border: 1px solid #00fb29; display: flex; flex-wrap: wrap; gap: 1rem; margin: 1rem 0; padding: 0.125rem; max-width: fit-content; width: 212px; } \u0026lt;/style\u0026gt; box box box box box box ","permalink":"https://www.webstf.nl/baseline/2021/#flexbox-gap","tags":null,"title":"Flexbox Gap"},{"categories":null,"content":"::file-selector-button selects the button part of a \u0026lt;input type=\u0026quot;file\u0026gt;\n1 2 3 4 5 6 7 8 9 10 \u0026lt;input type=\u0026#34;file\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; class=\u0026#34;styled\u0026#34;\u0026gt; \u0026lt;style\u0026gt; .styled::file-selector-button { background: rebeccapurple; border-radius: 0.25em; padding: 0.5em; } \u0026lt;/style\u0026gt; ","permalink":"https://www.webstf.nl/baseline/2021/#file-selector-button","tags":null,"title":"File Selector Button"},{"categories":null,"content":"font-family: system-ui uses the operating system default font for text.\nJust as this website does.\n","permalink":"https://www.webstf.nl/baseline/2021/#font-family-system","tags":null,"title":"Font Family System"},{"categories":null,"content":"Logical properties replace physical directions (top, left, right, bottom) with flow-relative equivalents (block-start, inline-end, etc.), making layouts that automatically adapt to different writing modes and text directions.\nPhysical Logical width / height inline-size / block-size margin-top margin-block-start padding-left padding-inline-start border-right border-inline-end top / left inset-block-start / inset-inline-start Shorthand\n1 2 3 margin-block: 1rem 2rem; /* block-start, block-end */ margin-inline: auto; /* inline-start and inline-end */ inset: 0; /* all four sides */ ","permalink":"https://www.webstf.nl/baseline/2021/#logical-properties","tags":null,"title":"Logical Properties"},{"categories":null,"content":"aspect-ratio sets the aspect ratio of the element. When using width and height aspect-ratio is overruled.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 \u0026lt;div class=\u0026#34;aspect-video box\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;aspect-video sized-box\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .box { background: #00fb29; padding: 1rem; } .sized-box { background: #00fb29; padding: 1rem; width: 100px; height: 100px; } .aspect-video { aspect-ratio: 16 / 9; } .aspect-square { aspect-ratio: 1; } \u0026lt;/style\u0026gt; box sized-box ","permalink":"https://www.webstf.nl/baseline/2021/#aspect-ratio","tags":null,"title":"Aspect Ratio"},{"categories":null,"content":"","permalink":"https://www.webstf.nl/baseline/2021/#counter-style","tags":null,"title":"Counter style"},{"categories":null,"content":"Some examples 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 \u0026lt;div class=\u0026#34;fits square\u0026#34;\u0026gt; Lorem ipsum dolor sit amet. \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;fits\u0026#34;\u0026gt; Lorem ipsum dolor sit amet, consectetur adipiscing elit \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;fits\u0026#34;\u0026gt; Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;fits\u0026#34;\u0026gt; Lorem ipsum dolor sit amet. \u0026lt;em\u0026gt;consectetur adipiscing elit\u0026lt;/em\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .fits { max-width: fit-content; padding: 1em; border-inline-start: 1px solid red; border-inline-end: 1px solid blue; margin-block-end: 0.5rem; \u0026amp;:has(em) { border-width: 1px; background-color: #495050; } } .square { aspect-ratio: 1; } \u0026lt;/style\u0026gt; Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet. consectetur adipiscing elit ","permalink":"https://www.webstf.nl/baseline/2021/#%!s(\u003cnil\u003e)","tags":null,"title":""},{"categories":null,"content":"The ::placeholder pseudo-element styles the placeholder text displayed in an \u0026lt;input\u0026gt; or \u0026lt;textarea\u0026gt; when the field is empty and no value has been entered. It allows the placeholder\u0026rsquo;s color, font, and other text properties to be customised.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 ::placeholder { color: #9ca3af; font-style: italic; } input::placeholder { color: rgb(0 0 0 / 0.4); font-size: 0.9em; } .search-input::placeholder { color: var(--color-placeholder); letter-spacing: 0.02em; } Browser default placeholder styling is typically a greyed-out version of the input\u0026rsquo;s text color. ::placeholder lets you override this with any color, opacity, font style, or text transformation. Only a subset of CSS properties apply — primarily text-related properties and opacity.\nA common accessibility consideration: placeholder text should not be used as a substitute for a visible label. The contrast ratio of placeholder text is often insufficient for users with low vision, and it disappears when the user starts typing — making it unsuitable as the only description of what a field expects. Visible, persistent labels are always preferable, with placeholder text serving as an optional additional hint.\n::placeholder is distinct from :placeholder-shown, which targets the input element itself while its placeholder is visible, rather than the placeholder text.\n","permalink":"https://www.webstf.nl/baseline/2020/#placeholder","tags":null,"title":"::placeholder"},{"categories":null,"content":"The :default pseudo-class matches form elements that are the default option in a group of related elements. The most common use cases are the default submit button in a form, the initially selected option in a \u0026lt;select\u0026gt;, and the pre-checked state of checkboxes and radio buttons.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /* Highlight the default submit button */ button:default { font-weight: bold; border-color: var(--color-primary); } /* Mark the pre-selected radio option */ input[type=\u0026#34;radio\u0026#34;]:default + label { color: var(--color-primary); } /* Indicate the initially selected option */ option:default { font-style: italic; } For \u0026lt;button\u0026gt; and \u0026lt;input type=\u0026quot;submit\u0026quot;\u0026gt; elements, :default matches the button that would be activated by pressing Enter in the form. For radio buttons and checkboxes, it matches the element that has the checked attribute in the HTML source — the initially selected state — even after the user has changed the selection. This distinguishes it from :checked, which always reflects the current state.\n:default is useful for communicating which choice is recommended or pre-selected, and for visually distinguishing the primary action in a form from secondary actions, providing helpful affordance to users without requiring additional markup or classes.\n","permalink":"https://www.webstf.nl/baseline/2020/#default","tags":null,"title":":default"},{"categories":null,"content":"The :focus-within pseudo-class matches an element when it or any of its descendants has focus. It propagates focus state up the DOM tree, allowing ancestor elements to respond to focus events deep within their subtree.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .form-field:focus-within label { color: var(--color-primary); font-weight: 600; } .search-box:focus-within { box-shadow: 0 0 0 3px var(--color-focus); border-color: var(--color-primary); } .card:focus-within { outline: 2px solid var(--color-focus); } nav:focus-within .nav-tooltip { display: block; } A common use case is styling a form field\u0026rsquo;s label or wrapper when the input inside receives focus — previously this required JavaScript to add and remove classes. :focus-within makes it a pure CSS operation.\nIt is also useful for showing contextual UI on keyboard navigation — a dropdown menu that reveals its options when any item inside it is focused, or a card that shows additional actions when a user tabs into it.\n:focus-within applies to any element that is a focusable descendant — inputs, buttons, links, and elements with tabindex. It remains active as long as any descendant retains focus, making it suitable for compound widgets where focus moves between several related elements.\n","permalink":"https://www.webstf.nl/baseline/2020/#focus-within","tags":null,"title":":focus-within"},{"categories":null,"content":"The :placeholder-shown pseudo-class matches an input or textarea element while its placeholder text is currently displayed — that is, when the field is empty and the user has not yet typed anything.\n1 2 3 4 5 6 7 8 9 input:placeholder-shown { border-color: #ccc; font-style: italic; } input:not(:placeholder-shown) { border-color: var(--color-primary); font-style: normal; } A well-known use of :placeholder-shown is the floating label pattern — a label that sits inside the input like placeholder text and floats above it when the user types:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .field { position: relative; margin-block-start: 1rem; margin-block-end: 0.5rem; label { color: var(--theme-inverted-text-color); margin: 0; position: absolute; top: 50%; left: 0.75rem; transform: translateY(-50%); transition: transform 0.2s ease, font-size 0.2s ease; } input { padding: 0.25lh 0.75rem; } } .field:has(input:not(:placeholder-shown)), .field:has(input:focus) { label { transform: translateY(-200%); font-size: 0.75rem; color: var(--color-primary); } } Label The trick relies on input:not(:placeholder-shown) to detect when the field has content, using a transparent placeholder to keep the pseudo-class active until the user types.\n:placeholder-shown is distinct from ::placeholder, which styles the placeholder text itself. :placeholder-shown targets the input element when placeholder text is visible; ::placeholder targets the placeholder text\u0026rsquo;s appearance directly.\n","permalink":"https://www.webstf.nl/baseline/2020/#placeholder-shown","tags":null,"title":":placeholder-shown"},{"categories":null,"content":"The :scope pseudo-class matches the element that is the scope reference point for a set of selectors. In a stylesheet, :scope is equivalent to :root — it matches the \u0026lt;html\u0026gt; element. Its primary utility is inside @scope blocks, where it refers to the scoping root element itself.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @scope (.card) { :scope { border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; } :scope:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 0.1); } p { margin: 0; } } Inside a @scope block, :scope targets the scoping root — the .card element in the example above — giving you a way to style the component root directly from within the scoped rule set, without breaking out of the scope or repeating the selector.\n:scope is also used in JavaScript with element.querySelectorAll(':scope \u0026gt; .child'), where it refers to the element on which querySelectorAll is called, making it possible to select direct children of that specific element rather than any matching elements in the document. This JavaScript use case predates the CSS @scope rule and remains the most widely encountered context for :scope.\n","permalink":"https://www.webstf.nl/baseline/2020/#scope-pseudo","tags":null,"title":":scope (pseudo-class)"},{"categories":null,"content":"The all property is a shorthand that resets every CSS property — except direction and unicode-bidi — to a specified value in a single declaration. It accepts the CSS-wide keywords: initial, inherit, unset, and revert.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .isolated-component { all: revert; /* Roll back to browser default styles */ } .hard-reset { all: initial; /* Reset everything to spec-defined initial values */ } .inherit-everything { all: inherit; /* Force all properties to inherit from parent */ } .unset-all { all: unset; /* inherit if naturally inherited, initial otherwise */ } all: revert is the most practical value for component isolation — it rolls styles back to what the browser\u0026rsquo;s default stylesheet would apply, giving a clean and familiar baseline. all: initial is more aggressive, stripping even browser defaults like display: block on \u0026lt;div\u0026gt;.\nall: unset combined with deliberate re-application of only the needed properties is a pattern used to build components that are immune to cascade bleed from external stylesheets. This is useful in widget libraries, email rendering contexts, or anywhere that a component must look consistent regardless of what styles surround it.\nNote that all does not reset custom properties (--*) — those are unaffected and continue to inherit normally.\n","permalink":"https://www.webstf.nl/baseline/2020/#all","tags":null,"title":"all"},{"categories":null,"content":"The background-blend-mode property sets how an element\u0026rsquo;s background layers blend with each other. When an element has multiple backgrounds — a combination of images, gradients, and a background color — background-blend-mode determines the compositing mode applied between them.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .duotone { background-image: url(\u0026#39;photo.jpg\u0026#39;), linear-gradient(to bottom, #f093fb, #f5576c); background-blend-mode: luminosity; background-size: cover; } .texture-overlay { background-image: url(\u0026#39;texture.png\u0026#39;), url(\u0026#39;base-image.jpg\u0026#39;); background-blend-mode: multiply, normal; } .color-tint { background-image: url(\u0026#39;photo.jpg\u0026#39;); background-color: oklch(60% 0.2 250 / 0.5); background-blend-mode: color; } The blend modes available are the same as those for mix-blend-mode: normal, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, and luminosity.\nbackground-blend-mode only blends backgrounds within the same element — it does not affect how the element blends with content behind it in the stacking context. For that, mix-blend-mode is used instead. When multiple background layers are present, comma-separated values assign a blend mode to each layer individually.\n","permalink":"https://www.webstf.nl/baseline/2020/#background-blend-mode","tags":null,"title":"background-blend-mode"},{"categories":null,"content":"The caret-color property sets the color of the text insertion cursor — the blinking vertical line that indicates where typed text will be inserted in an editable element. It applies to \u0026lt;input\u0026gt;, \u0026lt;textarea\u0026gt;, and any element with contenteditable.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 input, textarea { caret-color: var(--color-primary); } [contenteditable] { caret-color: #e74c3c; } .transparent-caret { caret-color: transparent; /* Hides the caret */ } .auto-caret { caret-color: auto; /* Default: uses currentColor */ } The default value auto causes the caret to use currentColor — the element\u0026rsquo;s text color — which is usually appropriate. Specifying an explicit color allows the caret to match a brand color or stand out against the input\u0026rsquo;s background color.\ncaret-color: transparent hides the caret entirely, which can be useful for custom cursor implementations or for building syntax-highlighted editors where a custom cursor is rendered separately.\nThe property accepts any valid CSS color value. It does not control the caret\u0026rsquo;s shape or blink rate — those remain under the control of the operating system. On some platforms, the caret may be rendered as a block or underline cursor rather than a vertical bar depending on system accessibility settings, and caret-color colors that cursor shape accordingly.\n","permalink":"https://www.webstf.nl/baseline/2020/#caret-color","tags":null,"title":"caret-color"},{"categories":null,"content":"CSS attribute selectors are case-sensitive by default for attribute values. The i flag (case-insensitive modifier) added to an attribute selector makes the value match case-insensitively, regardless of how the attribute value is written in the HTML.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /* Matches href values ending in .PDF, .pdf, .Pdf, etc. */ a[href$=\u0026#34;.pdf\u0026#34; i] { background: url(\u0026#39;pdf-icon.svg\u0026#39;) no-repeat left center; padding-left: 1.5rem; } /* Matches type=\u0026#34;text\u0026#34;, type=\u0026#34;TEXT\u0026#34;, type=\u0026#34;Text\u0026#34; */ input[type=\u0026#34;text\u0026#34; i] { border: 1px solid #ccc; } /* Matches any case variation of the data attribute value */ [data-theme=\u0026#34;dark\u0026#34; i] { color-scheme: dark; } The i flag is placed inside the brackets, after the value, separated by whitespace. It applies only to the attribute value — the attribute name itself is always matched case-insensitively in HTML (as HTML attribute names are case-insensitive by nature).\nA complementary s flag enforces case-sensitive matching explicitly, which is the default behaviour for HTML attribute values but useful in XML or SVG contexts where case sensitivity is more significant.\nCase-insensitive matching is most practical for file extension checks in href attributes, type attributes where user agents may produce inconsistent casing, and custom data-* attributes populated from external data sources where casing cannot be guaranteed.\n","permalink":"https://www.webstf.nl/baseline/2020/#case-insensitive-attributes","tags":null,"title":"Case-insensitive attribute selector"},{"categories":null,"content":"COLRv0 is the first version of the COLR (Color) OpenType font table format, which allows font glyphs to be composed of multiple colored layers. A COLRv0 font stores each color glyph as a stack of single-color glyph layers, with a corresponding CPAL (Color Palette) table that defines the palette of colors used.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .emoji { font-family: \u0026#39;NotoColorEmoji\u0026#39;, sans-serif; } /* Override palette colors using @font-palette-values */ @font-palette-values --custom-palette { font-family: \u0026#39;MyColorFont\u0026#39;; override-colors: 0 #ff4500, 1 #ffd700, 2 #1a1a1a; } .branded { font-family: \u0026#39;MyColorFont\u0026#39;; font-palette: --custom-palette; } COLRv0 fonts are widely supported across modern browsers and operating systems, and are the format behind many system emoji fonts. The palette colors defined in the font can be customised using @font-palette-values and the font-palette property, allowing a single font file to be recolored for different themes or brand contexts.\nCOLRv1 (the second version) significantly extends the format with gradient fills, compositing operations, and affine transforms within glyphs, enabling much richer color font artwork. COLRv0 remains relevant for broad compatibility and is the baseline color font format that all modern browsers support reliably.\n","permalink":"https://www.webstf.nl/baseline/2020/#colrv0","tags":null,"title":"COLRv0"},{"categories":null,"content":"The column-span property allows an element to span across all columns in a multi-column layout. It accepts two values: none (the default, where the element stays within a single column) and all (where the element spans the full width of the multi-column container).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .article { columns: 3; gap: 2rem; } .article h2 { column-span: all; /* Heading spans all three columns */ margin-bottom: 1rem; } .article .pull-quote { column-span: all; text-align: center; font-size: 1.5rem; padding: 2rem 0; } When an element with column-span: all appears in the flow, the content before it fills the columns above it, the spanning element is placed across the full width, and the content after it begins a new set of columns below. This is the standard pattern for chapter headings, pull quotes, and full-width images in editorial multi-column layouts.\nA spanning element effectively divides the multi-column container into two independent column sets — the content above the span and the content below it are balanced and laid out separately. The spanning element itself creates a new block formatting context.\nOnly none and all are currently defined — partial column spanning (spanning two of three columns, for example) is not supported.\n","permalink":"https://www.webstf.nl/baseline/2020/#column-span","tags":null,"title":"column-span"},{"categories":null,"content":"The revert keyword is a CSS-wide value that resets a property to the value it would have if no author styles had been applied — in other words, the browser\u0026rsquo;s built-in default.\nKeyword Resets to initial CSS specification default inherit Parent\u0026rsquo;s computed value unset Inherited or initial revert Browser user-agent stylesheet revert is uniquely useful because browser defaults are often more meaningful than specification defaults. For example, display: revert on a \u0026lt;div\u0026gt; gives back block, while display: initial gives inline.\n1 2 3 4 /* Remove all custom styles from an element */ .reset-me { all: revert; } This single declaration removes every custom style and restores the browser\u0026rsquo;s native look.\n","permalink":"https://www.webstf.nl/baseline/2020/#revert-value","tags":null,"title":"CSS revert"},{"categories":null,"content":"CSS.escape() is a static method on the CSS JavaScript interface that escapes a string for use as part of a CSS selector. It produces a string where any characters that would be invalid or have special meaning in a CSS selector are properly escaped, preventing selector injection errors or unintended selector behaviour.\n1 2 3 4 5 6 7 8 9 10 11 // Safely use a dynamic value in a selector const id = \u0026#39;1st-item\u0026#39;; // IDs starting with a digit are invalid unescaped const selector = \u0026#39;#\u0026#39; + CSS.escape(id); // Result: \u0026#39;#\\31 st-item\u0026#39; — valid CSS selector document.querySelector(selector); // Class names with special characters const className = \u0026#39;icon/home\u0026#39;; const el = document.querySelector(\u0026#39;.\u0026#39; + CSS.escape(className)); // Equivalent to: document.querySelector(\u0026#39;.icon\\\\/home\u0026#39;) Without CSS.escape(), building a CSS selector from user-provided or dynamic data requires manual escaping logic that is easy to get wrong. Characters that need escaping include digits at the start of an identifier, spaces, and many punctuation marks.\nCSS.escape() is particularly important for:\nIDs or class names that start with a digit Values containing spaces, slashes, colons, or other special characters Any selector component derived from user input or database content The method is part of the CSS Houdini family of APIs and is available in all modern browsers. For environments without CSS.escape(), the cssesc npm package provides equivalent functionality.\n","permalink":"https://www.webstf.nl/baseline/2020/#css-escape","tags":null,"title":"CSS.escape()"},{"categories":null,"content":"CSS.supports() is a static JavaScript method that checks whether the browser supports a given CSS feature, returning a boolean. It is the programmatic equivalent of the @supports at-rule, allowing feature detection to be performed in JavaScript rather than CSS.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Two-argument form: property and value if (CSS.supports(\u0026#39;display\u0026#39;, \u0026#39;grid\u0026#39;)) { console.log(\u0026#39;Grid is supported\u0026#39;); } if (CSS.supports(\u0026#39;color\u0026#39;, \u0026#39;oklch(55% 0.2 250)\u0026#39;)) { // Use oklch colors } // Single-argument form: full declaration or condition string if (CSS.supports(\u0026#39;(display: grid) and (gap: 1rem)\u0026#39;)) { // Both supported } if (CSS.supports(\u0026#39;selector(:has(+ *))\u0026#39;)) { // :has() relational selector is supported } if (CSS.supports(\u0026#39;not (display: grid)\u0026#39;)) { // Grid is not supported — apply fallback } The two-argument form takes a property name and a value as separate strings. The single-argument form accepts a condition string using the same syntax as @supports, including and, or, not, and the selector() function.\nCSS.supports() is useful when CSS feature detection needs to drive JavaScript behaviour — for example, deciding which polyfill to load, which library to initialise, or which code path to follow based on what the browser can handle. For purely CSS-driven behaviour differences, the @supports at-rule is preferable as it keeps the feature detection in CSS where it belongs.\n","permalink":"https://www.webstf.nl/baseline/2020/#css-supports","tags":null,"title":"CSS.supports()"},{"categories":null,"content":"display: flow-root creates a new block formatting context (BFC) for an element without any side effects — no clipping, no overflow change, no opacity shift. It is the clean, purpose-built solution for two classic CSS problems: containing floated children and preventing margin collapse.\n1 2 3 4 5 6 7 .container { display: flow-root; /* Contains floated children */ } .no-margin-collapse { display: flow-root; /* Prevents top/bottom margin from collapsing with children */ } Before display: flow-root, creating a new BFC required exploiting the side effects of other properties — overflow: hidden was the most common hack (it contains floats but clips content), along with float: left, display: inline-block, or position: absolute (each with their own layout consequences).\ndisplay: flow-root does exactly one thing: it establishes a new block formatting context. The element itself participates in the normal block flow of its parent, behaves as a block-level box, and has no visual side effects beyond containing its floats and preventing margin collapse.\n1 2 3 4 5 6 7 8 9 /* Before flow-root: hack with side effects */ .clearfix { overflow: hidden; /* Clips overflowing content — often undesirable */ } /* After flow-root: clean and intentional */ .clearfix { display: flow-root; } It is the modern, semantic replacement for clearfix patterns and overflow: hidden BFC hacks, and should be the default choice when a new block formatting context is all that is needed.\n","permalink":"https://www.webstf.nl/baseline/2020/#display-flow-root","tags":null,"title":"display: flow-root"},{"categories":null,"content":"The dominant-baseline property specifies the baseline used to align a box\u0026rsquo;s text and inline content. It is primarily relevant in SVG and in CSS contexts involving mixed scripts or vertical text, where different baselines — alphabetic, ideographic, central, mathematical — are appropriate for different content types.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 text { dominant-baseline: alphabetic; /* Default for horizontal Latin text */ } .math-label { dominant-baseline: mathematical; } .cjk-text { dominant-baseline: ideographic; } .centred-inline { dominant-baseline: central; } The dominant baseline establishes the reference point against which child elements align when alignment-baseline or vertical-align is set to baseline. Changing it shifts where that alignment anchor sits relative to the text.\nIn practice, dominant-baseline is most commonly encountered in SVG — particularly in data visualisations and icon systems — where precise text alignment relative to graphical elements is needed. In HTML, vertical-align and the newer text-box property typically handle similar concerns, but dominant-baseline remains the appropriate tool when working directly with SVG \u0026lt;text\u0026gt; elements or mixing SVG and HTML content.\n","permalink":"https://www.webstf.nl/baseline/2020/#dominant-baseline","tags":null,"title":"dominant-baseline"},{"categories":null,"content":"The font-display descriptor inside @font-face controls how the browser behaves during the period between a page loading and the custom font becoming available. It manages the trade-off between showing invisible text, showing a fallback font, or waiting for the custom font before rendering anything.\n1 2 3 4 5 @font-face { font-family: \u0026#39;MyFont\u0026#39;; src: url(\u0026#39;myfont.woff2\u0026#39;) format(\u0026#39;woff2\u0026#39;); font-display: swap; } The five values represent different strategies:\nauto leaves the decision to the browser — typically similar to block. block gives the font a short block period (usually 3 seconds) during which text is invisible, then swaps to the custom font when ready, with an indefinite swap period. swap immediately shows a fallback font and swaps to the custom font whenever it loads — no invisible text, but potentially a visible layout shift. fallback shows invisible text very briefly (100ms), then a fallback, and only swaps within a 3-second window — after that the fallback remains. optional is the most conservative — the browser decides whether to use the font based on connection speed, and may never swap if it hasn\u0026rsquo;t loaded quickly enough.\nswap is the most commonly recommended value for body text and important content — it avoids invisible text while still using the custom font. optional is best for non-essential decorative fonts where a layout shift would be disruptive. block is appropriate for icon fonts where showing a fallback letter character would be worse than brief invisible text.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-display","tags":null,"title":"font-display"},{"categories":null,"content":"The font-kerning property controls whether the browser uses kerning information stored in the font. Kerning adjusts the spacing between specific pairs of characters — such as \u0026ldquo;AV\u0026rdquo;, \u0026ldquo;To\u0026rdquo;, or \u0026ldquo;WA\u0026rdquo; — to produce optically even spacing that metric-based character widths alone cannot achieve.\n1 2 3 4 5 6 7 8 9 10 11 body { font-kerning: auto; /* Default: browser decides */ } .display-heading { font-kerning: normal; /* Always apply kerning */ } .monospaced-table { font-kerning: none; /* Disable kerning */ } auto leaves the decision to the browser, which typically applies kerning to larger text sizes but may skip it at small sizes for performance. normal explicitly enables kerning regardless of font size. none disables it entirely, which can be useful in contexts where consistent character widths are needed — such as tabular data or code.\nKerning is part of the broader OpenType feature system. It can also be controlled via font-feature-settings: \u0026quot;kern\u0026quot; 1 (enable) or \u0026quot;kern\u0026quot; 0 (disable), though font-kerning is the higher-level, more readable property for this specific feature.\nThe visual impact of kerning is most noticeable at large display sizes — headlines and hero text benefit most from careful kerning, while body text at typical reading sizes shows less difference.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-kerning","tags":null,"title":"font-kerning"},{"categories":null,"content":"The font-stretch property selects a font face based on its width — how condensed or expanded the letterforms are relative to the normal width of that typeface. It is the legacy name for what is now more accurately expressed as font-width, though both properties are equivalent and both are supported.\n1 2 3 4 5 6 7 8 9 10 11 .condensed { font-stretch: condensed; } .expanded { font-stretch: expanded; } .normal { font-stretch: normal; } Keyword values include ultra-condensed, extra-condensed, condensed, semi-condensed, normal, semi-expanded, expanded, extra-expanded, and ultra-expanded. These map to percentage values from 50% to 200%, which are also accepted directly:\n1 2 3 .variable-font { font-stretch: 75%; /* Condensed */ } For non-variable fonts, the browser selects the closest available face from the font family that matches the requested width. For variable fonts with a wdth axis, any percentage value within the supported range can be used for fine control.\nfont-stretch is most commonly used in editorial and display typography where condensed or expanded type is a deliberate stylistic choice — fitting more text into a constrained space, or creating a wide, expansive feel for headlines. For new code, font-width is the preferred property name as it more accurately describes what is being controlled, though font-stretch remains fully supported.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-stretch","tags":null,"title":"font-stretch"},{"categories":null,"content":"The font-variant-caps property selects alternate glyphs for capital letters, primarily for rendering small capitals. It controls which capitalisation variant the font uses, drawing from OpenType features built into the font file.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .small-caps { font-variant-caps: small-caps; /* Lowercase as small capitals */ } .all-small-caps { font-variant-caps: all-small-caps; /* Both upper and lower as small caps */ } .petite-caps { font-variant-caps: petite-caps; /* Smaller than small-caps */ } .unicase { font-variant-caps: unicase; /* Mix of small-caps and normal capitals */ } .titling { font-variant-caps: titling-caps; /* Glyphs designed for titling use */ } small-caps is the most commonly used value — it replaces lowercase letters with smaller versions of the capital letterforms, ideal for subheadings, abbreviations, and running headers. all-small-caps converts both uppercase and lowercase to small capitals, producing a more uniform typographic color.\npetite-caps targets fonts that include an even smaller caps variant, typically sized to match the x-height precisely. titling-caps accesses special capital letterforms designed for large display sizes, where standard capitals may appear too heavy or wide.\nIf the font does not include true small caps, the browser falls back to synthesised small caps — scaled-down regular capitals — which are typically less refined than designed ones. font-synthesis: small-caps controls whether this synthesis is permitted.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-variant-caps","tags":null,"title":"font-variant-caps"},{"categories":null,"content":"The font-variant-east-asian property controls the use of alternate glyphs for East Asian scripts — Chinese, Japanese, and Korean (CJK). It provides access to OpenType features that select between different regional glyph standards, width variants, and ruby annotation forms.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .simplified { font-variant-east-asian: simplified; /* Simplified Chinese glyphs */ } .traditional { font-variant-east-asian: traditional; /* Traditional Chinese glyphs */ } .jis78 { font-variant-east-asian: jis78; /* Japanese Industrial Standard 1978 */ } .proportional-width { font-variant-east-asian: proportional-width; } .full-width { font-variant-east-asian: full-width; } .ruby { font-variant-east-asian: ruby; /* Glyphs optimised for ruby annotations */ } The glyph set values — jis78, jis83, jis90, jis04, simplified, and traditional — select which regional or historical character standard to draw from. Many CJK characters have different acceptable forms depending on the target language and standard, and a font may include multiple variants.\nWidth values control whether CJK characters are set at full width (square ideographic spacing) or proportional width. ruby selects smaller, simplified glyph forms optimised for use as phonetic annotations above or beside CJK characters.\nThe effectiveness of all these values depends entirely on the font including the relevant OpenType features.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-variant-east-asian","tags":null,"title":"font-variant-east-asian"},{"categories":null,"content":"The font-variant-ligatures property controls which ligature types are used in text rendering. Ligatures are single glyphs that replace two or more adjacent characters — \u0026ldquo;fi\u0026rdquo; and \u0026ldquo;fl\u0026rdquo; are the most common examples, where the dot of the \u0026ldquo;i\u0026rdquo; or the crossbar of the \u0026ldquo;f\u0026rdquo; would otherwise collide awkwardly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 body { font-variant-ligatures: normal; /* Browser default behaviour */ } .editorial { font-variant-ligatures: common-ligatures discretionary-ligatures; } .no-ligatures { font-variant-ligatures: no-common-ligatures; } .historical { font-variant-ligatures: historical-ligatures; } The ligature categories are: common ligatures (fi, fl, ff, ffi, ffl), discretionary ligatures (decorative combinations the font designer has chosen to include), historical ligatures (older forms no longer in common use), and contextual alternates (glyphs that change shape based on surrounding characters).\nCommon ligatures are enabled by default in most browsers. Discretionary and historical ligatures are off by default and must be explicitly enabled. Contextual alternates (contextual / no-contextual) control whether characters adapt their shape based on adjacent characters — common in script and handwriting fonts.\nThe normal value restores typical browser behaviour (common ligatures on, others off). none disables all ligatures. Individual categories can be combined: font-variant-ligatures: common-ligatures discretionary-ligatures contextual.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-variant-ligatures","tags":null,"title":"font-variant-ligatures"},{"categories":null,"content":"The font-variant-numeric property controls the rendering of digits and related glyphs — fractions, ordinals, and zero — using OpenType numeric features built into the font. It allows for fine typographic control over how numbers appear in different contexts.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .oldstyle { font-variant-numeric: oldstyle-nums; /* Lowercase-style figures */ } .tabular { font-variant-numeric: tabular-nums; /* Fixed-width digits for alignment */ } .lining-tabular { font-variant-numeric: lining-nums tabular-nums; /* Tables and data */ } .fractions { font-variant-numeric: diagonal-fractions; } .ordinals { font-variant-numeric: ordinal; /* e.g. 1st, 2nd with raised suffix */ } .slashed-zero { font-variant-numeric: slashed-zero; /* Distinguish 0 from O */ } The two primary axes are figure style and figure spacing. Figure style: lining-nums (uniform height, sits on the baseline) vs oldstyle-nums (varying heights, like lowercase letters). Figure spacing: proportional-nums (varying widths, like regular text) vs tabular-nums (fixed widths for column alignment).\nFraction variants — diagonal-fractions and stacked-fractions — render combinations like \u0026ldquo;1/2\u0026rdquo; as proper typographic fractions using dedicated glyphs. ordinal enables superscript suffixes for ordinal numbers. slashed-zero adds a diagonal slash to the zero glyph to distinguish it from the capital O, useful in code and technical contexts.\nMultiple values can be combined: font-variant-numeric: oldstyle-nums proportional-nums diagonal-fractions.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-variant-numeric","tags":null,"title":"font-variant-numeric"},{"categories":null,"content":"The font-variation-settings property provides low-level access to variable font axes using four-character axis tags and numeric values. Variable fonts contain multiple design variations along one or more axes — weight, width, slant, optical size, and custom axes — all within a single font file.\n1 2 3 4 5 6 7 8 9 10 11 .variable-heading { font-variation-settings: \u0026#39;wght\u0026#39; 650, \u0026#39;wdth\u0026#39; 85, \u0026#39;ital\u0026#39; 1; } .optical-size { font-variation-settings: \u0026#39;opsz\u0026#39; 48; /* Optimised for 48px display size */ } .custom-axis { font-variation-settings: \u0026#39;BNCE\u0026#39; 50; /* Custom \u0026#34;bounce\u0026#34; axis */ } Standard registered axes have four-character lowercase tags: wght (weight), wdth (width), ital (italic), slnt (slant), and opsz (optical size). Custom axes defined by the type designer use uppercase tags.\nWhere high-level CSS properties exist for standard axes — font-weight for wght, font-width/font-stretch for wdth, font-style for ital and slnt, font-optical-sizing for opsz — those should be preferred. font-variation-settings is most useful for custom axes that have no CSS property equivalent, or for animating axes with @keyframes:\n1 2 3 4 @keyframes weight-pulse { from { font-variation-settings: \u0026#39;wght\u0026#39; 300; } to { font-variation-settings: \u0026#39;wght\u0026#39; 800; } } Note that font-variation-settings is not inherited as individual axis values — it replaces the whole value, so setting a partial list will reset any axes not included to their defaults.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-variation-settings","tags":null,"title":"font-variation-settings"},{"categories":null,"content":"The :host pseudo-class, used inside a shadow DOM stylesheet, selects the shadow host — the element in the light DOM to which the shadow root is attached. It allows a web component to style its own host element from within its encapsulated styles.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /* Inside a web component\u0026#39;s shadow DOM */ :host { display: block; /* Shadow hosts are inline by default */ contain: content; border-radius: 8px; overflow: hidden; } :host(:hover) { box-shadow: 0 4px 12px rgb(0 0 0 / 0.15); } :host([disabled]) { opacity: 0.5; pointer-events: none; } :host(.large) { font-size: 1.25rem; } :host accepts an optional selector argument — :host(selector) — which makes it match only when the host element also matches the given selector. This is how a component responds to attributes, classes, or states set by the consumer of the component from outside the shadow DOM.\nStyles set on the host element from outside the shadow DOM (in the light DOM) take priority over :host styles, because the shadow DOM encapsulation does not prevent the host element from being styled externally — it only encapsulates the component\u0026rsquo;s internal styles from affecting the outside. This means :host styles act as sensible defaults that consumers can override.\n","permalink":"https://www.webstf.nl/baseline/2020/#host","tags":null,"title":"Host"},{"categories":null,"content":"The hsl() function defines colors using the HSL color model — Hue, Saturation, and Lightness — which maps colors to a cylindrical representation that is more intuitive for human reasoning about color relationships than RGB.\n1 2 3 4 5 6 7 8 9 10 11 .primary { color: hsl(220 80% 55%); } .muted { color: hsl(220 20% 60%); } .transparent-bg { background-color: hsl(220 80% 55% / 0.2); } Hue is expressed as an angle (0–360°) around the color wheel — 0° and 360° are red, 120° is green, 240° is blue. Saturation ranges from 0% (grey) to 100% (fully saturated). Lightness ranges from 0% (black) to 100% (white), with 50% being the pure color.\nThe modern syntax uses space-separated values without commas, and alpha is added with /: hsl(220 80% 55% / 0.5). The legacy comma syntax hsl(220, 80%, 55%) and hsla(220, 80%, 55%, 0.5) are still supported.\nHSL is more intuitive than rgb() for creating color variations — lightening, darkening, or desaturating a color means adjusting a single value. However, HSL is not perceptually uniform — the same lightness value produces colors that appear visually brighter or darker depending on the hue. For perceptually consistent color work, oklch() is a better choice as its lightness channel produces more visually predictable results across all hues.\n","permalink":"https://www.webstf.nl/baseline/2020/#hsl","tags":null,"title":"HSL"},{"categories":null,"content":"The isolation property controls whether an element creates a new stacking context, independent of whether it has any properties that would typically trigger one. Setting isolation: isolate forces the creation of a new stacking context without any visual side effects.\n1 2 3 .component { isolation: isolate; } The primary use case is containing mix-blend-mode. By default, an element with mix-blend-mode blends with everything behind it in its stacking context — including content outside a parent component. Wrapping the component in an element with isolation: isolate creates a new stacking context boundary, so blend modes inside it only composite against content within the isolated group, not against the wider page.\n1 2 3 4 5 6 7 .card-grid { isolation: isolate; /* Blend modes inside cards won\u0026#39;t bleed out */ } .card-image { mix-blend-mode: multiply; } A second use case is predictable z-index management. Because isolation: isolate creates a new stacking context without affecting opacity, transform, or other properties, it is a clean way to contain the z-index scope of a component — preventing internal z-index values from competing with unrelated elements on the page.\nisolation: auto (the default) means no new stacking context is created unless another property requires it. isolation: isolate is the only other value.\n","permalink":"https://www.webstf.nl/baseline/2020/#isolation","tags":null,"title":"isolation"},{"categories":null,"content":"The direction and unicode-bidi properties control the text direction and bidirectional text handling for an element. Together they can override the Unicode bidirectional algorithm to force a specific layout direction, independent of the language of the content.\ndirection sets the base text direction: ltr (left to right, the default for most languages) or rtl (right to left, for Arabic, Hebrew, and other RTL languages).\n1 2 3 4 5 6 7 .rtl-block { direction: rtl; } .force-ltr { direction: ltr; } unicode-bidi controls how the Unicode bidirectional algorithm is applied. The key values are normal (default), embed, isolate, bidi-override, and isolate-override.\n1 2 3 4 5 6 7 8 .bidi-override { direction: rtl; unicode-bidi: bidi-override; /* Force all characters to RTL, ignore Unicode bidi */ } .isolate { unicode-bidi: isolate; /* Treat content as isolated from surrounding bidi context */ } bidi-override is the most aggressive — it ignores the Unicode bidirectional properties of individual characters and forces everything to the specified direction. isolate isolates the element\u0026rsquo;s content from the surrounding bidi context, preventing its directionality from affecting adjacent content.\nIn HTML, the dir attribute and the \u0026lt;bdi\u0026gt; and \u0026lt;bdo\u0026gt; elements handle bidirectional text semantically and are generally preferred over CSS direction overrides, which lack semantic meaning and can affect accessibility. The CSS properties are most appropriate for dynamic direction changes via JavaScript or for styling text in complex bidi layouts.\n","permalink":"https://www.webstf.nl/baseline/2020/#layout-direction-override","tags":null,"title":"Layout direction override"},{"categories":null,"content":"CSS provides two pseudo-classes specifically for styling hyperlinks based on their visited state: :link and :visited. These are the original link-related pseudo-classes and form part of the classic \u0026ldquo;LVHA\u0026rdquo; ordering rule for link styles.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /* Unvisited links */ a:link { color: #0066cc; text-decoration: underline; } /* Visited links */ a:visited { color: #551a8b; } /* Hovered links */ a:hover { color: #004499; text-decoration: none; } /* Active (being clicked) links */ a:active { color: #cc0000; } :link matches anchor elements with an href attribute that have not been visited. :visited matches those that the browser history records as visited. The traditional recommendation to declare these in LVHA order (:link, :visited, :hover, :active) ensures each state overrides the previous correctly due to specificity being equal.\nFor privacy reasons, browsers significantly restrict what CSS properties can be applied to :visited links. Only color, background-color, border-color, outline-color, and the color values of column-rule-color and text-decoration-color can be changed — and JavaScript cannot read the computed styles of visited links to prevent history sniffing. These restrictions mean :visited styling is limited to color changes only.\n:any-link is a newer pseudo-class that matches any element with an href — both :link and :visited — making it a convenient selector when you want to style all links regardless of visited state.\n","permalink":"https://www.webstf.nl/baseline/2020/#link-selectors","tags":null,"title":"Link selectors"},{"categories":null,"content":"min-content and max-content are intrinsic size keywords that size an element based on the natural dimensions of its content, rather than the available space in the container.\nmin-content sizes the element to the smallest it can be without causing overflow — for text, this is the width of the longest unbreakable word or inline element. For images and replaced elements, it is the element\u0026rsquo;s natural size.\nmax-content sizes the element to be as wide as it needs to be to fit all content on a single line, without any wrapping. For text this means no line breaks at all; for a container it means the sum of all its content\u0026rsquo;s natural widths.\n1 2 3 4 5 6 7 8 9 10 11 12 .shrink-wrap { width: min-content; /* Shrinks to longest word */ } .expand-to-content { width: max-content; /* Expands to fit all content on one line */ } .grid { /* Column sized to the longest cell content */ grid-template-columns: max-content 1fr; } Both keywords are valid in width, height, min-width, max-width, and as grid track sizes. In grid layout they are particularly powerful — minmax(min-content, 1fr) creates a column that will not shrink below its minimum content size but will grow to fill available space.\nThey are also used implicitly: a floated element or an element with display: inline-block uses max-content sizing by default, while a flex or grid item may be constrained to min-content when space is tight.\n","permalink":"https://www.webstf.nl/baseline/2020/#min-max-content","tags":null,"title":"min-content and max-content"},{"categories":null,"content":"The mix-blend-mode property sets how an element\u0026rsquo;s content blends with the content behind it in the stacking context — the elements and backgrounds beneath it in the document. It applies Photoshop-style compositing modes to web elements.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .overlay-text { mix-blend-mode: multiply; /* Darkens where text overlaps content below */ } .light-effect { mix-blend-mode: screen; /* Lightens, like projecting light */ } .contrast-boost { mix-blend-mode: overlay; } .color-tint { mix-blend-mode: color; /* Applies color while preserving luminosity below */ } .knockout-text { mix-blend-mode: difference; /* Inverts colors beneath the element */ } The available blend modes are: normal, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, and luminosity.\nmix-blend-mode blends the element with everything behind it in its stacking context. The isolation property on an ancestor can contain blending to a specific subtree — isolation: isolate prevents an element\u0026rsquo;s children from blending with content outside of it.\nUnlike background-blend-mode, which composites multiple backgrounds within a single element, mix-blend-mode composites the element itself against the content that exists behind it in the rendering stack. The two can be combined for complex multi-layer effects.\n","permalink":"https://www.webstf.nl/baseline/2020/#mix-blend-mode","tags":null,"title":"mix-blend-mode"},{"categories":null,"content":"The prefers-color-scheme media feature detects whether the user\u0026rsquo;s operating system is set to a light or dark color theme, allowing CSS to adapt the page\u0026rsquo;s color palette automatically to match the system preference.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 :root { --color-bg: #ffffff; --color-text: #1a1a1a; --color-primary: oklch(55% 0.2 250); --color-border: #e0e0e0; } @media (prefers-color-scheme: dark) { :root { --color-bg: #121212; --color-text: #f0f0f0; --color-primary: oklch(70% 0.2 250); --color-border: #333333; } } body { background-color: var(--color-bg); color: var(--color-text); } The two values are light (the user prefers a light theme, or has no preference) and dark (the user prefers a dark theme). The no-preference value was defined in earlier drafts but has been removed — if the user has not set a preference, light is the fallback.\nUsing CSS custom properties as the adapter layer — defining all color values as variables on :root and overriding them in the dark mode media query — is the cleanest approach. All components and elements that reference these variables automatically update without needing their own media query blocks.\nprefers-color-scheme works alongside color-scheme — the media query handles custom design colors, while color-scheme handles browser-rendered UI elements. Together they provide a complete dark mode implementation. The preference can also be detected and observed in JavaScript via window.matchMedia('(prefers-color-scheme: dark)').\n","permalink":"https://www.webstf.nl/baseline/2020/#prefers-color-scheme","tags":null,"title":"prefers-color-scheme media query"},{"categories":null,"content":"The prefers-reduced-motion media feature detects whether the user has enabled the \u0026ldquo;reduce motion\u0026rdquo; accessibility setting in their operating system. Respecting this preference is an important accessibility requirement for users who experience discomfort, dizziness, or seizures from animated content.\n1 2 3 4 5 6 7 8 9 10 @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } The two values are no-preference (the user has not requested reduced motion, the default) and reduce (the user has enabled the preference).\nA more nuanced approach is to default to no animation and only add motion for users who are fine with it — this is sometimes called the \u0026ldquo;motion-first\u0026rdquo; pattern:\n1 2 3 4 5 6 7 8 9 10 .animated { /* No animation by default */ } @media (prefers-reduced-motion: no-preference) { .animated { transition: transform 0.3s ease; animation: slide-in 0.4s ease forwards; } } This approach is particularly valuable for users who rely on reduced motion but whose browser may not perfectly apply the global suppression override.\nNot all motion needs to be removed — the preference is for \u0026ldquo;reduced\u0026rdquo; motion, not \u0026ldquo;no\u0026rdquo; motion. Subtle, functional transitions (like a button changing color) may still be appropriate, while vestibular-triggering effects (large-scale movement, parallax, spinning, zooming) should be suppressed.\n","permalink":"https://www.webstf.nl/baseline/2020/#prefers-reduced-motion","tags":null,"title":"prefers-reduced-motion media query"},{"categories":null,"content":"The rgb() function defines colors using the RGB color model — Red, Green, and Blue — expressed as values from 0 to 255 (integers) or 0% to 100% (percentages). It is one of the foundational color functions in CSS, corresponding directly to how displays mix colored light.\n1 2 3 4 5 6 7 8 9 10 11 .red { color: rgb(220 38 38); } .semi-transparent { background: rgb(0 0 0 / 0.5); } .blue-tint { background: rgb(59 130 246 / 0.15); } The modern syntax uses space-separated values, with alpha added after a slash: rgb(255 0 0 / 0.5). The legacy comma syntax rgb(255, 0, 0) and rgba(255, 0, 0, 0.5) are still supported — rgba() is simply an older alias for rgb() with alpha.\nHex color notation (#rrggbb) is shorthand for rgb() — #3b82f6 and rgb(59 130 246) are identical values. The eight-digit hex format #rrggbbaa includes alpha.\nRGB operates in the sRGB color space, which is the standard for most web content and corresponds to the color gamut that standard displays can reproduce. For colors beyond sRGB — the wider gamut available on modern displays — color(display-p3 r g b) or oklch() are more appropriate. For colors that are easier to reason about in terms of hue and lightness relationships, hsl() or oklch() are more intuitive than raw RGB values.\n","permalink":"https://www.webstf.nl/baseline/2020/#rgb","tags":null,"title":"RGB"},{"categories":null,"content":"Safe area inset environment variables provide the measurements of the areas on a device screen that are obscured by physical features — such as a notch, home indicator, rounded corners, or camera cutout — and should not be covered by interactive content. They are accessed using the env() function.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .nav-bar { padding-top: env(safe-area-inset-top); padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); } .footer { padding-bottom: env(safe-area-inset-bottom); } .floating-button { bottom: calc(1.5rem + env(safe-area-inset-bottom)); right: calc(1rem + env(safe-area-inset-right)); } The four variables are safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, and safe-area-inset-left. On devices without obstructions they resolve to 0.\nTo activate safe area insets on iOS devices, the viewport meta tag must include viewport-fit=cover:\n1 \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1, viewport-fit=cover\u0026#34;\u0026gt; Without this, the browser automatically constrains content to the safe area and the inset values are always zero. With viewport-fit=cover, content can extend under the notch and home indicator, and the CSS inset variables become meaningful for pushing interactive elements clear of obstructions.\nThe env() function accepts a fallback value as a second argument: env(safe-area-inset-bottom, 0px).\n","permalink":"https://www.webstf.nl/baseline/2020/#safe-area-inset","tags":null,"title":"Safe area inset environment variables"},{"categories":null,"content":"The will-change property hints to the browser that an element is expected to change in a specific way, allowing it to apply optimisations in advance — typically by promoting the element to its own compositor layer. This can improve animation performance by avoiding layout and paint recalculations during the animation.\n1 2 3 4 5 6 7 .animated-card { will-change: transform; } .fading-overlay { will-change: opacity; } The most common values are transform and opacity, which are the two properties most frequently animated and most amenable to GPU compositing. Other accepted values include scroll-position, contents, and any animatable CSS property name.\nwill-change should be used sparingly and only on elements that genuinely need it. Applying it broadly — such as * { will-change: transform } — consumes significant memory and GPU resources, as each hinted element gets its own compositor layer. The browser\u0026rsquo;s default behaviour already handles most animations well without hints.\nBest practice is to apply will-change just before an animation starts and remove it afterward, using JavaScript:\n1 2 3 4 5 6 element.addEventListener(\u0026#39;mouseenter\u0026#39;, () =\u0026gt; { element.style.willChange = \u0026#39;transform\u0026#39;; }); element.addEventListener(\u0026#39;animationend\u0026#39;, () =\u0026gt; { element.style.willChange = \u0026#39;auto\u0026#39;; }); will-change also creates a new stacking context, which can affect z-index behaviour in the same way as transform and opacity.\n","permalink":"https://www.webstf.nl/baseline/2020/#will-change","tags":null,"title":"will-change"},{"categories":null,"content":"The Q unit stands for quarter-millimetre (0.25mm). It is a physical length unit intended primarily for print stylesheets, where exact physical dimensions are critical.\nQ value Millimetres Points (approx) 4Q 1mm ~2.8pt 16Q 4mm ~11.3pt 48Q 12mm ~34pt ","permalink":"https://www.webstf.nl/baseline/2020/#q-unit","tags":null,"title":" Q Unit"},{"categories":null,"content":"Setting background-clip: text clips any background — gradient, image, or color — to the exact shape of the text characters, creating eye-catching typographic effects in pure CSS.\nSetting color: transparent lets the clipped background show through the text.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 h1 { background: linear-gradient(135deg, #f093fb, #f5576c); -webkit-background-clip: text; background-clip: text; color: transparent; } @keyframes shift { from { background-position: 0% 50%; } to { background-position: 100% 50%; } } .animated { background: linear-gradient(90deg, #4facfe, #00f2fe, #4facfe); background-size: 200%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shift 3s linear infinite; } Animated text background ","permalink":"https://www.webstf.nl/baseline/2020/#background-clip-text","tags":null,"title":"Background clip"},{"categories":null,"content":"conic-gradient() produces a gradient where color transitions happen around a central point, rather than along a line (linear) or from a centre outward (radial).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* This creates a simple pie chart with three equal segments. */ .pie { background: conic-gradient(red 0deg 90deg, blue 90deg 180deg, green 180deg 360deg); border-radius: 50%; width: 200px; height: 200px; } /* 40% red, 35% blue, 25% green */ .chart { background: conic-gradient(red 40%, blue 75%, green 100%); border-radius: 50%; } /* color wheels */ .wheel { background: conic-gradient(hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%)); border-radius: 50%; } /* Repeating conic gradients */ .checkerboard { background: repeating-conic-gradient(black 0% 25%, white 25% 50%); background-size: 40px 40px; } ","permalink":"https://www.webstf.nl/baseline/2020/#conic-gradients","tags":null,"title":"Conic gradients"},{"categories":null,"content":"font-optical-sizing enables or disables automatic optical size adjustments in variable fonts that include an opsz axis. When active, the browser fine-tunes letterform details to match the rendered size.\n","permalink":"https://www.webstf.nl/baseline/2020/#font-optical-sizing","tags":null,"title":"Font optical sizing"},{"categories":null,"content":"line-break sets how to break lines of Chinese, Japanese, or Korean (CJK) text when working with punctuation and symbols. See: line-break ","permalink":"https://www.webstf.nl/baseline/2020/#line-break","tags":null,"title":"Line break"},{"categories":null,"content":"min(), max(), and clamp() These three comparison functions make responsive design much more fluid by letting any CSS property value adapt within defined boundaries.\nmin() — Use the Smallest Value\n1 2 3 .container { width: min(90vw, 800px); } The container is 90% of the viewport, but never wider than 800px.\nmax() — Use the Largest Value\n1 2 3 p { font-size: max(1rem, 2.5vw); } Font size scales with the viewport but is always at least 1rem.\nclamp() — Stay Within a Range\n1 2 3 h1 { font-size: clamp(1.5rem, 5vw, 3rem); } clamp(min, preferred, max) — the font is 5vw, but never smaller than 1.5rem or larger than 3rem.\nAll three functions accept mixed units in a single expression:\n1 padding: clamp(1rem, 2vw + 0.5rem, 3rem); A single clamp() declaration often replaces two or three @media breakpoints for font sizes and spacing.\n","permalink":"https://www.webstf.nl/baseline/2020/#min-max-clamp","tags":null,"title":"min(), max(), and clamp()"},{"categories":null,"content":"The overflow property is a shorthand for overflow-x and overflow-y. Understanding its two-value syntax and newer values gives you precise control over scrollable and clipped content.\n1 2 3 4 5 /* Both axes set to scroll */ overflow: scroll; /* x-axis: hidden, y-axis: auto */ overflow: hidden auto; When two values are given, the first is overflow-x and the second is overflow-y.\nValue Behaviour visible Content overflows — no clipping hidden Clips and creates block formatting context scroll Always shows scrollbars auto Scrollbar appears only when needed clip Clips without creating a scroll container ","permalink":"https://www.webstf.nl/baseline/2020/#overflow-shorthand","tags":null,"title":"Overflow shorthand"},{"categories":null,"content":"page-orientation is a descriptor used inside @page rules that rotates the page content when printing. It is particularly useful for print stylesheets that include wide tables or charts alongside normal portrait content.\n1 2 3 4 5 6 7 @page landscape-page { page-orientation: rotate-right; } .wide-table { page: landscape-page; } This rotates any .wide-table element onto a landscape page when printing.\nValue Effect upright Default, no rotation rotate-left Rotates content 90° counter-clockwise rotate-right Rotates content 90° clockwise For full landscape pages, combine page-orientation with the size descriptor:\n1 2 3 4 @page wide { size: A4 landscape; page-orientation: upright; } ","permalink":"https://www.webstf.nl/baseline/2020/#page-orientation","tags":null,"title":"Page orientation"},{"categories":null,"content":"The shadow DOM normally blocks external CSS. Shadow Parts give web component authors a way to expose specific internal elements for styling, without opening the entire shadow tree.\nInside the shadow DOM template, add the part attribute to elements you want to expose:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \u0026lt;template\u0026gt; \u0026lt;button part=\u0026#34;button\u0026#34;\u0026gt; \u0026lt;span part=\u0026#34;label\u0026#34;\u0026gt;Click me\u0026lt;/span\u0026gt; \u0026lt;/button\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style\u0026gt; my-button::part(button) { background: #0077ff; border-radius: 4px; border: none; } my-button::part(label) { font-weight: 700; color: white; } \u0026lt;/style\u0026gt; ::part() does not support combinators or pseudo-classes (except :hover and :focus ).\nWith Shadow Parts component authors retain control of their internals while allowing consumers to apply brand and theme styles cleanly.\n","permalink":"https://www.webstf.nl/baseline/2020/#shadow-parts","tags":null,"title":"Shadow Parts"},{"categories":null,"content":"The steps() timing function divides an animation into equal, discrete jumps instead of a smooth transition. It is the secret behind sprite-sheet animations and typewriter effects.\n1 2 3 .sprite { animation: walk 0.8s steps(8) infinite; } This splits the animation into 8 equal steps, jumping between positions rather than interpolating smoothly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 .character { width: 64px; height: 64px; background-image: url(\u0026#34;sprite.png\u0026#34;); animation: run 0.6s steps(6) infinite; } @keyframes run { from { background-position: 0 0; } to { background-position: -384px 0; } /* 6 x 64px */ } /* typewriter effect */ .typewriter { overflow: hidden; width: 0; animation: type 3s steps(30) forwards; } @keyframes type { to { width: 30ch; } } These are shorthand values: step-start equals steps(1, start) and step-end equals steps(1, end).\n","permalink":"https://www.webstf.nl/baseline/2020/#steps-easing","tags":null,"title":"steps() Easing"},{"categories":null,"content":"text-orientation is used in combination with writing-mode: vertical-* to control how characters are oriented — whether they stand upright or are rotated sideways.\n","permalink":"https://www.webstf.nl/baseline/2020/#text-orientation","tags":null,"title":"Text orientation"},{"categories":null,"content":"text-underline-offset sets the distance of the line when using text-decoration: underline\n1 2 3 4 5 6 7 8 \u0026lt;style\u0026gt; .underline { text-decoration: underline var(--theme-gradient-light); text-underline-offset: 1rem; } \u0026lt;/style\u0026gt; \u0026lt;p class=\u0026#34;underline\u0026#34;\u0026gt;Some underlined text\u0026lt;/p\u0026gt; Some underlined text\n","permalink":"https://www.webstf.nl/baseline/2020/#text-underline-offset","tags":null,"title":"Text underline offset"},{"categories":null,"content":"text-underline-position sets the position of the line when using text-decoration: underline\n1 2 3 4 5 6 7 8 9 \u0026lt;style\u0026gt; .underline { text-decoration: underline var(--theme-gradient-light); text-underline-offset: 1rem; text-underline-position: under; } \u0026lt;/style\u0026gt; \u0026lt;p class=\u0026#34;underline\u0026#34;\u0026gt;Some underlined text\u0026lt;/p\u0026gt; Some underlined text\n","permalink":"https://www.webstf.nl/baseline/2020/#text-underline-position","tags":null,"title":"Text underline position"},{"categories":null,"content":"Page break properties control where the browser or printer is allowed or required to break a document when paginating — during printing or when using paged media. The modern properties are break-before, break-after, and break-inside, which work across paged media, multi-column layouts, and CSS regions.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 h1, h2 { break-after: avoid; /* Don\u0026#39;t break immediately after a heading */ } .chapter { break-before: page; /* Always start on a new page */ } .figure { break-inside: avoid; /* Keep figure and caption together */ } table { break-inside: avoid; /* Don\u0026#39;t split the table across pages */ } break-before and break-after control breaks at the edges of an element. Values include auto (default), avoid, avoid-page, page, left, right, column, and avoid-column. break-inside applies to breaks within the element, accepting auto or avoid.\nwidows and orphans provide complementary control at the line level — specifying the minimum number of lines that must appear before or after a page break within a paragraph.\nThe legacy page-break-before, page-break-after, and page-break-inside properties are aliases for the modern equivalents and remain supported for backwards compatibility, but the break-* properties are preferred as they also work in multi-column contexts.\n","permalink":"https://www.webstf.nl/baseline/2019/#page-breaks","tags":null,"title":"Page breaks"},{"categories":null,"content":"Sticky positioning is a hybrid between relative and fixed positioning. A sticky element behaves like a relatively positioned element — it scrolls with the page — until it reaches a specified threshold, at which point it \u0026ldquo;sticks\u0026rdquo; and behaves like a fixed element within its scrolling container, until its parent scrolls out of view.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .sticky-header { position: sticky; top: 0; /* Sticks when the top of the element hits the top of the viewport */ z-index: 10; } .sticky-sidebar { position: sticky; top: 2rem; /* Sticks 2rem from the top */ align-self: start; /* Essential in flex/grid to prevent stretching */ } .sticky-section-label { position: sticky; top: 60px; /* Sticks below a 60px fixed nav */ } The top, right, bottom, or left value specifies the offset at which the element sticks. At least one must be set — without it, the element behaves like position: relative with no sticking.\nA sticky element\u0026rsquo;s sticky behaviour is contained within its scroll container and is bounded by its parent. Once the parent scrolls completely out of view, the sticky element scrolls away with it. This makes sticky positioning naturally scoped — a section label sticks while its section is visible, then exits with the section.\nCommon gotchas include: a parent with overflow: hidden or overflow: auto breaks stickiness by making the parent the scroll container; a flex or grid container may need align-self: start on the sticky child to prevent it from stretching to the container height; and scroll-margin-top on :target elements should account for sticky header height.\n","permalink":"https://www.webstf.nl/baseline/2019/#sticky-positioning","tags":null,"title":"Sticky positioning"},{"categories":null,"content":"The ::selection pseudo-element applies styles to the portion of a document that has been highlighted by the user — typically by clicking and dragging with a mouse or selecting text with a keyboard. It allows the default browser selection highlight to be replaced with custom colors.\n1 2 3 4 5 6 7 8 9 ::selection { background-color: oklch(70% 0.2 250); color: white; } .code-block::selection { background-color: oklch(80% 0.15 130); color: #1a1a1a; } Only a limited set of properties are supported on ::selection: color, background-color, text-decoration and its longhands, text-shadow, and stroke-color. The background shorthand is not supported — use background-color explicitly.\nCustom selection colors are a subtle but effective branding touch, particularly on editorial or portfolio sites. It is important to maintain sufficient contrast between the selection background and text color to keep selected text readable — the same accessibility considerations that apply to normal text apply here.\n::selection matches the active selection. Inactive selections (when the window loses focus) are not separately targetable in CSS and typically revert to a greyed-out system default.\n","permalink":"https://www.webstf.nl/baseline/2018/#selection","tags":null,"title":"::selection"},{"categories":null,"content":"The font-variation-settings property provides low-level access to variable font axes using four-character axis tags and numeric values. Variable fonts contain multiple design variations along one or more axes — weight, width, slant, optical size, and custom axes — all within a single font file.\n1 2 3 4 5 6 7 8 9 10 11 .variable-heading { font-variation-settings: \u0026#39;wght\u0026#39; 650, \u0026#39;wdth\u0026#39; 85, \u0026#39;ital\u0026#39; 1; } .optical-size { font-variation-settings: \u0026#39;opsz\u0026#39; 48; /* Optimised for 48px display size */ } .custom-axis { font-variation-settings: \u0026#39;BNCE\u0026#39; 50; /* Custom \u0026#34;bounce\u0026#34; axis */ } Standard registered axes have four-character lowercase tags: wght (weight), wdth (width), ital (italic), slnt (slant), and opsz (optical size). Custom axes defined by the type designer use uppercase tags.\nWhere high-level CSS properties exist for standard axes — font-weight for wght, font-width/font-stretch for wdth, font-style for ital and slnt, font-optical-sizing for opsz — those should be preferred. font-variation-settings is most useful for custom axes that have no CSS property equivalent, or for animating axes with @keyframes:\n1 2 3 4 @keyframes weight-pulse { from { font-variation-settings: \u0026#39;wght\u0026#39; 300; } to { font-variation-settings: \u0026#39;wght\u0026#39; 800; } } Note that font-variation-settings is not inherited as individual axis values — it replaces the whole value, so setting a partial list will reset any axes not included to their defaults.\n","permalink":"https://www.webstf.nl/baseline/2018/#font-variation-settings","tags":null,"title":"font-variation-settings"},{"categories":null,"content":"Interaction media features query the nature of the user\u0026rsquo;s input mechanism — specifically whether a pointer device is available and how precise it is, and whether the device supports hovering. They allow styles to adapt to touch-first versus mouse-first interfaces independently of screen size.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /* Coarse pointer: touch screens, game controllers */ @media (pointer: coarse) { .button { min-height: 44px; min-width: 44px; padding: 0.75rem 1.25rem; } } /* Fine pointer: mouse, trackpad, stylus */ @media (pointer: fine) { .button { padding: 0.5rem 1rem; } } /* No pointer device at all */ @media (pointer: none) { .hover-menu { display: none; } } The hover media feature checks whether the primary input can hover:\n1 2 3 4 5 6 7 8 9 10 11 12 13 @media (hover: hover) { .card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgb(0 0 0 / 0.15); } } @media (hover: none) { /* Touch devices: don\u0026#39;t rely on hover for revealing content */ .dropdown-trigger + .dropdown { display: block; } } any-pointer and any-hover work the same way but check any available input device, not just the primary one — useful on devices that support both touch and a mouse simultaneously. Interaction media queries are more reliable than screen-width breakpoints for distinguishing touch from mouse interfaces, since a large touchscreen and a small laptop may both have similar widths but very different interaction models.\n","permalink":"https://www.webstf.nl/baseline/2018/#interaction","tags":null,"title":"Interaction media queries"},{"categories":null,"content":"CSS numeric factory functions are static methods on the CSS JavaScript interface that create CSSUnitValue objects — typed numeric values with units — for use with the CSS Typed Object Model (Typed OM). They provide a type-safe, object-based alternative to string manipulation when working with CSS values in JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // Instead of: element.style.width = \u0026#39;100px\u0026#39; element.attributeStyleMap.set(\u0026#39;width\u0026#39;, CSS.px(100)); // Factory functions for common units CSS.px(16) // 16px CSS.em(1.5) // 1.5em CSS.rem(2) // 2rem CSS.percent(50) // 50% CSS.vw(100) // 100vw CSS.vh(100) // 100vh CSS.deg(45) // 45deg CSS.rad(Math.PI) // ~3.14rad CSS.ms(300) // 300ms CSS.s(1.5) // 1.5s CSS.fr(1) // 1fr CSS.number(0.5) // 0.5 (unitless) // Values can be used directly const width = CSS.px(200); console.log(width.value); // 200 console.log(width.unit); // \u0026#39;px\u0026#39; // Arithmetic on typed values const margin = CSS.px(16).add(CSS.rem(1)); Factory functions are part of the CSS Houdini Typed OM — a lower-level, more performant API for reading and writing CSS values compared to string-based element.style manipulation. They avoid parsing errors from string concatenation and enable arithmetic between typed values.\nThe Typed OM is available in modern browsers but is primarily useful in performance-critical animation and layout code, or in Worklets (Paint, Layout, Animation) where the Typed OM is the only available API.\n","permalink":"https://www.webstf.nl/baseline/2018/#numeric-factory-functions","tags":null,"title":"Numeric factory functions"},{"categories":null,"content":"The overflow-wrap property controls whether the browser may break a word mid-character to prevent it from overflowing its container. It applies when a single word or unbroken string is too long to fit on a line and would otherwise overflow.\n1 2 3 4 5 6 7 8 9 10 11 .user-content { overflow-wrap: break-word; } .strict { overflow-wrap: anywhere; } .default { overflow-wrap: normal; } normal (the default) only breaks at allowed break points — spaces, hyphens, and other natural word boundaries. Long URLs or strings without spaces will overflow. break-word allows the browser to break within a word, but only as a last resort when the word would otherwise overflow — natural break points are still preferred. anywhere is similar but also allows breaks to influence min-content sizing, making it more aggressive in constrained layouts.\nThe difference between break-word and anywhere is most visible in min-content calculations: break-word does not affect how the browser calculates the minimum width of an element, while anywhere does, potentially resulting in narrower minimum widths.\noverflow-wrap was historically also known as word-wrap — a non-standard property introduced by Microsoft. Both names are still supported by browsers, but overflow-wrap is the standardised name and should be preferred for new code.\n","permalink":"https://www.webstf.nl/baseline/2018/#overflow-wrap","tags":null,"title":"overflow-wrap"},{"categories":null,"content":"overflow: overlay displays scrollbars that float over the content rather than occupying layout space, preserving the element\u0026rsquo;s full width. It is now an alias for overflow: auto in most modern browsers.\n1 2 3 .scrollable { overflow: overlay; } On systems with overlay scrollbars (macOS, iOS, some Linux), this produces scrollbars that appear on hover without shifting the layout.\noverflow: overlay was a non-standard value introduced by WebKit. It has since been standardised as an alias for overflow: auto in the CSS spec — browsers that show overlay scrollbars will do so automatically with auto.\nOn macOS and mobile devices, overflow: auto already shows overlay scrollbars. For Windows-style persistent scrollbars, use the scrollbar-gutter property instead:\n1 2 3 4 .scrollable { overflow: auto; scrollbar-gutter: stable; /* reserve space for scrollbar even when not scrolling */ } overflow: overlay is deprecated. New projects should use overflow: auto and scrollbar-gutter for reliable cross-platform behaviour.\n","permalink":"https://www.webstf.nl/baseline/2018/#overflow-overlay","tags":null,"title":"overflow: overlay"},{"categories":null,"content":"The resolution media feature targets displays based on their pixel density, typically used to serve higher-resolution assets to retina and high-DPI screens. The standard syntax uses dppx (dots per pixel) or dpi (dots per inch) units with the modern range syntax.\n1 2 3 4 5 6 @media (resolution \u0026gt;= 2dppx) { .logo { background-image: url(\u0026#39;logo@2x.png\u0026#39;); background-size: contain; } } Historically, browser support for resolution was inconsistent, and two vendor-prefixed alternatives were widely used to fill the gaps:\n1 2 3 4 5 6 7 8 /* Standard */ @media (resolution \u0026gt;= 2dppx) { } /* WebKit/Blink prefix (older Safari, Chrome) */ @media (-webkit-min-device-pixel-ratio: 2) { } /* Legacy Firefox */ @media (min--moz-device-pixel-ratio: 2) { } For broad compatibility during the period when prefixed forms were necessary, stylesheets often combined all three in a single query using a comma-separated list:\n1 2 3 4 5 6 7 8 @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (resolution \u0026gt;= 2dppx) { .logo { background-image: url(\u0026#39;logo@2x.png\u0026#39;); } } Modern browsers all support the unprefixed resolution feature, so the prefixed forms are no longer required for new projects. However, they remain relevant when maintaining legacy stylesheets or supporting older browser versions. The -webkit-min-device-pixel-ratio form in particular persisted in Safari longer than other prefixes, making it the one most likely to still be found in production codebases.\n","permalink":"https://www.webstf.nl/baseline/2018/#resolution-compat","tags":null,"title":"resolution media query (compatibility prefixes)"},{"categories":null,"content":"The border-image property replaces an element\u0026rsquo;s standard border with a specified image, slice of an image, or CSS gradient. It tiles, stretches, or repeats a source image to fill the border area, allowing for decorative borders that cannot be achieved with border-style alone.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .ornate { border: 20px solid transparent; border-image: url(\u0026#39;ornate-border.png\u0026#39;) 30 round; } .gradient-border { border: 4px solid transparent; border-image: linear-gradient(135deg, #f093fb, #f5576c) 1; } .badge { border: 8px solid transparent; border-image: url(\u0026#39;badge-frame.png\u0026#39;) 40 stretch; } border-image is a shorthand for border-image-source, border-image-slice, border-image-width, border-image-outset, and border-image-repeat.\nborder-image-slice divides the source image into nine regions using four inset values — four corners, four edges, and the centre. The number values correspond to pixels in raster images or coordinate units in SVG. The corners are placed at the corners of the border; the edges tile or stretch along the sides.\nborder-image-repeat controls how edge regions fill the space: stretch (default), repeat, round (scales to fit a whole number of tiles), or space (distributes tiles with spacing).\nUsing a gradient as the border-image-source with a slice value of 1 is a clean technique for gradient borders — the background-clip: padding-box pattern achieves a similar result but border-image is more direct for this specific use case.\n","permalink":"https://www.webstf.nl/baseline/2017/#border-image","tags":null,"title":"Border images"},{"categories":null,"content":"The column-fill property controls how content is distributed across columns in a multi-column layout. It determines whether columns are filled sequentially (one after another) or balanced (approximately equal height across all columns).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .article { columns: 3; column-fill: balance; /* Default: distribute content evenly */ } .sequential-columns { columns: 3; height: 400px; column-fill: auto; /* Fill first column, then second, etc. */ } .balanced-fixed { columns: 3; height: 600px; column-fill: balance-all; /* Balance across all columns, even with height set */ } balance (the default when no height is set) distributes content as evenly as possible across all columns, so they all end at approximately the same point. This is the typical typographic behaviour for flowing text articles.\nauto fills columns sequentially from the first — the first column fills to the container\u0026rsquo;s height, then the second, and so on. This requires a height to be set on the container; without one, auto behaves like balance.\nbalance-all is similar to balance but forces balancing even when a height is explicitly set — in balance mode, a fixed height can cause sequential overflow into subsequent columns.\ncolumn-fill interacts with column-span — a spanning element always breaks the column balance at its position, creating separate balanced groups above and below it.\n","permalink":"https://www.webstf.nl/baseline/2017/#column-fill","tags":null,"title":"column-fill"},{"categories":null,"content":"The cursor property sets the type of cursor shown when the mouse pointer is over an element. It communicates affordance — what the user can do with the element — and is an important part of interactive UI design.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .button { cursor: pointer; /* Hand cursor, signals clickability */ } .disabled { cursor: not-allowed; } .draggable { cursor: grab; } .draggable:active { cursor: grabbing; } .resizable { cursor: ew-resize; /* East-west resize arrow */ } .text-area { cursor: text; } The most commonly used keyword values include default (the standard arrow), pointer (pointing hand for links and buttons), text (I-beam for text), move (four-directional arrow), grab and grabbing (for draggable elements), not-allowed (blocked action), wait (loading), crosshair, and a full set of directional resize cursors (n-resize, ew-resize, nwse-resize, etc.).\nCustom cursors can be specified using url(), with fallback keywords:\n1 2 3 .custom { cursor: url(\u0026#39;cursor.png\u0026#39;) 8 8, pointer; } The two numbers after the URL are the cursor\u0026rsquo;s hotspot coordinates — the pixel offset of the active click point from the top-left of the image. A fallback keyword is required after any url() value.\ncursor: none hides the cursor entirely, which is occasionally used in games or immersive experiences that render their own custom cursor with JavaScript.\n","permalink":"https://www.webstf.nl/baseline/2017/#cursor","tags":null,"title":"Cursor styles"},{"categories":null,"content":"Custom properties — also called CSS variables — are author-defined properties that store values for reuse throughout a stylesheet. They are declared with a -- prefix and accessed using the var() function. Unlike preprocessor variables, they are live in the browser: they participate in the cascade, can be inherited, and can be changed at runtime with JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 :root { --color-primary: oklch(55% 0.2 250); --color-text: #1a1a1a; --spacing-md: 1rem; --border-radius: 6px; } .button { background: var(--color-primary); padding: var(--spacing-md) calc(var(--spacing-md) * 2); border-radius: var(--border-radius); } var() accepts a fallback as a second argument, used when the custom property is not defined:\n1 2 3 .element { color: var(--color-accent, var(--color-primary, blue)); } Custom properties are scoped to the element they are declared on and inherited by descendants — re-declaring a property on a child element overrides it for that subtree:\n1 2 3 .card { --color-primary: oklch(60% 0.2 30); /* Overrides for this card */ } They can be set and read in JavaScript:\n1 2 element.style.setProperty(\u0026#39;--color-primary\u0026#39;, \u0026#39;red\u0026#39;); const value = getComputedStyle(element).getPropertyValue(\u0026#39;--color-primary\u0026#39;); @property enables typed custom properties with defined syntax, initial values, and inheritance behaviour — allowing them to be animated and providing better tooling support.\n","permalink":"https://www.webstf.nl/baseline/2017/#custom-properties","tags":null,"title":"Custom properties"},{"categories":null,"content":"The display: table value and its related table display values allow non-table HTML elements to be styled with the same layout behaviour as HTML table elements. This gives access to table formatting — automatic column width distribution, row height equalisation, and cell alignment — without requiring \u0026lt;table\u0026gt; markup.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .table { display: table; width: 100%; } .row { display: table-row; } .cell { display: table-cell; padding: 0.5rem 1rem; vertical-align: middle; } .col-group { display: table-column-group; } .caption { display: table-caption; caption-side: bottom; } The full set of table display values mirrors the HTML table element hierarchy: table, table-row, table-cell, table-row-group, table-header-group, table-footer-group, table-column, table-column-group, and table-caption.\nA key feature of table layout is that cells in the same column automatically share the same width, and cells in the same row share the same height — behaviour that is difficult to replicate with other layout models without JavaScript. vertical-align on display: table-cell elements controls the vertical alignment of content within the cell, making it one of the older techniques for vertical centring.\nTable display values were commonly used for layout before flexbox and grid were available, and remain useful in specific scenarios — particularly equal-height columns and vertical alignment — though flexbox and grid are now generally preferred for new layouts.\n","permalink":"https://www.webstf.nl/baseline/2017/#display-table","tags":null,"title":"display: table"},{"categories":null,"content":"The font-feature-settings property provides low-level control over OpenType font features using four-character feature tags. It is the escape hatch for accessing any OpenType feature that does not have a dedicated CSS property — though where a higher-level property exists (font-variant-*, font-kerning, etc.), that should be preferred.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .stylistic-set { font-feature-settings: \u0026#39;ss01\u0026#39; 1, \u0026#39;ss02\u0026#39; 1; } .oldstyle-nums { font-feature-settings: \u0026#39;onum\u0026#39; 1; } .no-ligatures { font-feature-settings: \u0026#39;liga\u0026#39; 0, \u0026#39;clig\u0026#39; 0; } .small-caps { font-feature-settings: \u0026#39;smcp\u0026#39; 1; } .tabular { font-feature-settings: \u0026#39;tnum\u0026#39; 1; } Each feature is identified by its four-character OpenType tag followed by a value — 1 to enable, 0 to disable, or an integer for features that accept a selection index (like cv01 through cv99 character variants). Multiple features are comma-separated.\nCommon tags include kern (kerning), liga (standard ligatures), clig (contextual ligatures), onum (oldstyle numerals), tnum (tabular numerals), smcp (small capitals), sups (superscripts), subs (subscripts), frac (fractions), zero (slashed zero), and ss01–ss20 (stylistic sets).\nBecause font-feature-settings operates at the lowest level of font feature control, it overrides settings made by higher-level properties when both target the same feature. It is best used specifically for features that have no CSS property equivalent, leaving the rest to font-variant-* properties.\n","permalink":"https://www.webstf.nl/baseline/2017/#font-feature-settings","tags":null,"title":"font-feature-settings"},{"categories":null,"content":"CSS Grid Layout is a two-dimensional layout system that allows elements to be placed into rows and columns simultaneously. A grid container is created with display: grid, and its children become grid items that can be precisely placed within the defined grid structure.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .layout { display: grid; grid-template-columns: 240px 1fr; grid-template-rows: auto 1fr auto; gap: 1.5rem; min-height: 100vh; } .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.5rem; } grid-template-columns and grid-template-rows define the track structure. The fr unit distributes remaining space proportionally. repeat() creates repeated track patterns, and auto-fill or auto-fit with minmax() creates responsive grids that reflow without media queries.\nItems can be explicitly placed using grid-column and grid-row with line numbers or named lines:\n1 2 3 4 5 6 7 .header { grid-column: 1 / -1; /* Span all columns */ } .sidebar { grid-row: 2 / 4; } grid-template-areas provides a visual syntax for layout:\n1 2 3 4 5 6 7 8 9 10 11 12 13 .page { display: grid; grid-template-areas: \u0026#39;header header\u0026#39; \u0026#39;sidebar main\u0026#39; \u0026#39;footer footer\u0026#39;; grid-template-columns: 240px 1fr; } .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; } .footer { grid-area: footer; } Grid and flexbox complement each other — grid excels at two-dimensional layouts where both axes matter simultaneously, while flexbox is better suited to one-dimensional flow along a single axis.\n","permalink":"https://www.webstf.nl/baseline/2017/#grid","tags":null,"title":"Grid"},{"categories":null,"content":"CSS multi-column layout flows content through multiple vertical columns, similar to a newspaper or magazine layout. It is established with columns, column-count, or column-width on a container element, and content flows automatically from one column to the next.\n1 2 3 4 5 6 7 8 9 10 11 12 .article { columns: 3; /* Three equal-width columns */ gap: 2rem; } .narrow-text { column-width: 20rem; /* As many columns as fit at this width */ } .max-columns { column-count: 4; /* Exactly four columns */ } columns is a shorthand for column-count and column-width. When both are specified, column-width acts as a minimum width and column-count as a maximum — the browser creates as many columns of at least that width as will fit, up to the specified count.\nColumn gaps and rules are controlled with gap (or column-gap) and column-rule:\n1 2 3 4 5 .article { columns: 3; column-gap: 3rem; column-rule: 1px solid #e0e0e0; /* Vertical divider between columns */ } column-rule is a shorthand for column-rule-width, column-rule-style, and column-rule-color, and works like border but draws a line between columns without affecting layout.\nContent can span all columns with column-span: all, and column breaking can be controlled with break-before, break-after, and break-inside. Multi-column layout is well suited for editorial content, card grids with fixed widths, and any context where a newspaper-style flow is appropriate.\n","permalink":"https://www.webstf.nl/baseline/2017/#multi-column","tags":null,"title":"Multi-column layout"},{"categories":null,"content":"Text stroke and fill effects on HTML text elements are achieved using -webkit-text-stroke and -webkit-text-fill-color — properties that originated as WebKit extensions and have never been standardised, but are now supported across all major browsers due to their widespread adoption.\n-webkit-text-stroke adds an outline to text glyphs. It is a shorthand for -webkit-text-stroke-width and -webkit-text-stroke-color. -webkit-text-fill-color sets the fill color of text independently of the color property, allowing the two to differ.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .outlined-text { -webkit-text-stroke: 2px #1a1a1a; -webkit-text-fill-color: transparent; /* Outline only, no fill */ } .gradient-text { background: linear-gradient(135deg, #f093fb, #f5576c); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .thick-stroke { -webkit-text-stroke-width: 3px; -webkit-text-stroke-color: navy; -webkit-text-fill-color: white; } The gradient text technique combines -webkit-background-clip: text with -webkit-text-fill-color: transparent to let a background gradient show through the text shape. The unprefixed background-clip: text is now also supported in modern browsers, so both forms are often included for completeness.\nBecause these properties remain prefixed and are not part of any CSS standard, they should be treated as progressive enhancements. The paint-order property on SVG text offers a more standards-aligned approach to stroke and fill ordering, though it does not directly replicate all of these effects on HTML elements.\n","permalink":"https://www.webstf.nl/baseline/2017/#text-stroke-fill","tags":null,"title":"Text stroke and fill (compatibility prefixes)"},{"categories":null,"content":"The text-justify property controls the justification algorithm used when text-align: justify is set. It determines how the browser distributes the extra space needed to make lines fill their container — whether by adjusting word spacing, character spacing, or using language-appropriate methods.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .article { text-align: justify; text-justify: auto; /* Default: browser chooses the best method */ } .inter-word { text-align: justify; text-justify: inter-word; /* Space added between words only */ } .inter-character { text-align: justify; text-justify: inter-character; /* Space distributed between characters */ } .none { text-align: justify; text-justify: none; /* Disables justification, same as text-align: start */ } auto allows the browser to choose the most appropriate method based on the content\u0026rsquo;s language and script. For Latin text this is typically inter-word; for CJK text it is inter-character, which distributes space between individual characters — the typographically appropriate approach for ideographic scripts.\ninter-word is the standard approach for Latin text — only the spaces between words are stretched, maintaining normal character spacing within words. inter-character (equivalent to the older distribute value) spreads space across all character boundaries, which can look unusual for Latin text but is conventional for CJK.\nBrowser support for values other than auto and none is inconsistent, making auto the most reliable choice in practice.\n","permalink":"https://www.webstf.nl/baseline/2017/#text-justify","tags":null,"title":"text-justify"},{"categories":null,"content":"The writing-mode property sets whether text runs horizontally or vertically, and the direction of the block flow. It is the fundamental property for supporting vertical writing systems used in East Asian typography, and is also used for creative typographic effects with Latin text.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .horizontal { writing-mode: horizontal-tb; /* Default: left to right, top to bottom */ } .vertical-right { writing-mode: vertical-rl; /* Top to bottom, right to left column flow */ } .vertical-left { writing-mode: vertical-lr; /* Top to bottom, left to right column flow */ } .sideways { writing-mode: sideways-rl; /* Rotates the entire line sideways */ } horizontal-tb is the default for most languages — text runs left to right (or right to left for Arabic/Hebrew, controlled by direction) and lines stack top to bottom.\nvertical-rl is the standard mode for traditional Japanese and Chinese vertical text — characters flow top to bottom, and columns progress from right to left. vertical-lr is less common but used in some contexts where columns progress left to right.\nChanging writing-mode affects the entire box model — the block axis and inline axis swap, so width controls what was previously height, and height controls what was previously width. Logical properties (block-size, inline-size, margin-block, padding-inline, etc.) automatically adapt to the writing mode, making them the preferred choice for components that may be used in multiple writing directions.\nwriting-mode also affects form controls, making it possible to create vertical range sliders and progress bars.\n","permalink":"https://www.webstf.nl/baseline/2017/#writing-mode","tags":null,"title":"writing-mode"},{"categories":null,"content":"SVG 1.1 defined its own set of writing-mode values — lr, lr-tb, rl, rl-tb, tb, and tb-rl — which predate the CSS Writing Modes specification and used different naming conventions. These values were adopted into CSS for SVG compatibility but are now considered legacy and deprecated in favour of the standard CSS values.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* SVG 1.1 legacy values (deprecated) */ text { writing-mode: tb-rl; /* Top to bottom, right to left — now: vertical-rl */ } text { writing-mode: lr-tb; /* Left to right, top to bottom — now: horizontal-tb */ } /* Modern CSS equivalents */ text { writing-mode: vertical-rl; } text { writing-mode: horizontal-tb; } The mapping between legacy and modern values is straightforward: lr and lr-tb map to horizontal-tb; rl and rl-tb also map to horizontal-tb (with direction: rtl for right-to-left); tb and tb-rl map to vertical-rl.\nModern browsers continue to support the legacy SVG values for backwards compatibility, but they should not be used in new code. The CSS Writing Modes values (horizontal-tb, vertical-rl, vertical-lr, sideways-rl, sideways-lr) are the standard and work consistently in both HTML and SVG contexts.\nWhen authoring inline SVG within HTML documents, the CSS writing mode properties apply and the modern values should be used.\n","permalink":"https://www.webstf.nl/baseline/2017/#writing-mode-svg-values","tags":null,"title":"writing-mode SVG 1.1 values"},{"categories":null,"content":"The @font-face at-rule loads a custom font from a file or URL and makes it available to use in the stylesheet via a font-family name. It is how web fonts — whether self-hosted or served from a font service — are registered with the browser.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @font-face { font-family: \u0026#39;MyFont\u0026#39;; src: url(\u0026#39;myfont.woff2\u0026#39;) format(\u0026#39;woff2\u0026#39;), url(\u0026#39;myfont.woff\u0026#39;) format(\u0026#39;woff\u0026#39;); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: \u0026#39;MyFont\u0026#39;; src: url(\u0026#39;myfont-bold.woff2\u0026#39;) format(\u0026#39;woff2\u0026#39;); font-weight: 700; font-style: normal; font-display: swap; } Multiple @font-face blocks with the same font-family name but different font-weight or font-style values build up a font family with distinct faces. The browser selects the correct face automatically based on the element\u0026rsquo;s applied weight and style.\nThe src descriptor lists font files in order of preference — the browser uses the first format it supports. WOFF2 is the most compressed modern format and should be listed first; WOFF is a fallback for older browsers.\nfont-display controls how the browser handles the period before the font loads. swap shows a fallback font immediately and swaps to the custom font when it arrives, minimising invisible text. optional only uses the custom font if it loads within a very short window, avoiding layout shifts on slow connections.\nVariable fonts are declared with a single @font-face block covering a range of weights or styles: font-weight: 100 900.\n","permalink":"https://www.webstf.nl/baseline/2016/#font-face","tags":null,"title":"@font-face"},{"categories":null,"content":"The @supports at-rule allows CSS to be applied conditionally based on whether the browser supports a given property and value. During the period when @supports itself was new, some browsers required a -webkit- prefixed form to use it — though this was short-lived and never widely needed.\nMore relevant in practice is using @supports to test for prefixed properties alongside their standard equivalents, providing a compatibility layer during the transition period when a feature was only available with a vendor prefix.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /* Test for standard property support */ @supports (display: grid) { .layout { display: grid; grid-template-columns: repeat(3, 1fr); } } /* Test for a prefixed property */ @supports (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)) { .glass { -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); } } /* Negation — apply when a property is NOT supported */ @supports not (display: grid) { .layout { display: flex; flex-wrap: wrap; } } @supports queries can be combined with and, or, and not operators. The selector() function extends this to test whether a given selector syntax is supported:\n1 2 3 4 5 @supports selector(:has(+ *)) { .parent:has(+ .sibling) { margin-bottom: 0; } } @supports is the recommended approach for progressive enhancement — applying modern CSS only where it is supported, with simpler fallbacks for browsers that do not yet understand the feature.\n","permalink":"https://www.webstf.nl/baseline/2016/#supports-compat","tags":null,"title":"@supports (compatibility prefix)"},{"categories":null,"content":"The background-repeat property controls whether and how a background image tiles to fill the element\u0026rsquo;s background area. It accepts keyword values for the horizontal and vertical axes independently, or shorthand keywords that apply to both.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .tiled { background-image: url(\u0026#39;tile.png\u0026#39;); background-repeat: repeat; /* Default: tiles in both directions */ } .horizontal-stripe { background-repeat: repeat-x; /* Tiles horizontally only */ } .vertical-stripe { background-repeat: repeat-y; /* Tiles vertically only */ } .no-repeat { background-repeat: no-repeat; /* Single instance, no tiling */ } .spaced { background-repeat: space; /* Tiles without clipping, spaced evenly */ } .rounded { background-repeat: round; /* Tiles without clipping, scaled to fit whole number */ } repeat is the default and tiles the image in both directions, clipping the image at the edges if it doesn\u0026rsquo;t divide evenly. no-repeat places the image once at the position specified by background-position.\nspace distributes tiles evenly across the background area without clipping — the first and last tiles touch the edges, and any remaining space is distributed between tiles. round scales the image up or down to ensure a whole number of tiles fit without clipping or gaps.\nTwo-value syntax sets horizontal and vertical behaviour independently: background-repeat: repeat no-repeat tiles only horizontally. When multiple background layers are used, comma-separated values configure each layer separately.\n","permalink":"https://www.webstf.nl/baseline/2016/#background-repeat","tags":null,"title":"background-repeat"},{"categories":null,"content":"The unset keyword resets a property to its natural behaviour: if the property is naturally inherited, it acts like inherit; if it is not naturally inherited, it acts like initial. It is one of the CSS-wide keywords available on every property.\n1 2 3 4 5 6 7 8 9 10 11 .reset-color { color: unset; /* Inherits from parent (color is inherited) */ } .reset-display { display: unset; /* Resets to initial (display is not inherited) */ } .reset-all { all: unset; /* Reset every property to its natural behaviour */ } unset is useful when you want a property to behave as if no author styles had been applied — naturally inherited properties will pick up their value from the cascade, while non-inherited properties will use their specification default.\nThe distinction from the other CSS-wide keywords is important: initial always resets to the spec-defined initial value regardless of inheritance; inherit always forces inheritance regardless of whether the property naturally inherits; unset respects the property\u0026rsquo;s natural inheritance behaviour. revert is similar to unset but rolls back to the browser default stylesheet value rather than the spec initial value, which is often more useful in practice.\nall: unset is a common pattern for creating isolated components that should not be affected by surrounding styles, though all: revert is typically more practical as it preserves sensible browser defaults.\n","permalink":"https://www.webstf.nl/baseline/2016/#unset-value","tags":null,"title":"unset"},{"categories":null,"content":"The ::first-letter pseudo-element targets the first letter (or character) of the first line of a block-level element, allowing it to be styled independently. It is commonly used to create drop caps — an enlarged initial letter at the start of a paragraph — a typographic convention with a long history in print design.\n1 2 3 4 5 6 7 8 9 10 11 12 13 p::first-letter { font-size: 3em; font-weight: bold; float: left; line-height: 0.8; margin-right: 0.1em; } .article-intro::first-letter { font-family: \u0026#39;Georgia\u0026#39;, serif; font-size: 4rem; color: var(--color-accent); } Only a limited set of CSS properties apply to ::first-letter: font properties, text decoration, color, background, margins, padding, borders, float, vertical-align (when not floated), line-height, and word-spacing. The pseudo-element is not available on inline elements.\nThe browser determines what counts as the \u0026ldquo;first letter\u0026rdquo; — punctuation marks immediately preceding the first letter are included in the pseudo-element, and for languages where a letter is composed of multiple characters (like digraphs), the entire letter may be selected.\n","permalink":"https://www.webstf.nl/baseline/2015/#first-letter","tags":null,"title":"::first-letter"},{"categories":null,"content":"The ::first-line pseudo-element matches the first formatted line of a block-level element. The selection is dynamic — if the container is resized and more or fewer words fit on the first line, the styled range adjusts automatically to always cover exactly the first rendered line.\n1 2 3 4 5 6 7 8 9 p::first-line { font-variant: small-caps; letter-spacing: 0.05em; } .article-body::first-line { font-weight: bold; color: var(--color-primary); } Only a subset of CSS properties can be applied to ::first-line: font properties, color, background, word-spacing, letter-spacing, text-decoration, text-transform, line-height, text-shadow, and vertical-align. Layout properties that would affect the shape of the line — like margin or padding — are not permitted.\n::first-line applies only to block containers. It has no effect on inline elements. The styled range is determined after all other layout and text-wrapping has been calculated, so the pseudo-element always reflects the actual rendered first line rather than a fixed character count.\n","permalink":"https://www.webstf.nl/baseline/2015/#first-line","tags":null,"title":"::first-line"},{"categories":null,"content":"The :empty pseudo-class matches elements that have no children — no child elements, no text nodes, and no whitespace. It allows you to apply styles specifically when an element contains nothing, which is useful for hiding placeholder containers or applying different styles to empty states.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .notification-badge:empty { display: none; } .card-body:empty { padding: 0; } td:empty { background: repeating-linear-gradient( 45deg, transparent, transparent 4px, #f0f0f0 4px, #f0f0f0 8px ); } An important subtlety: :empty does not match an element that contains only whitespace in most contexts — a single space or newline between opening and closing tags means the element is not considered empty. This can make :empty behave unexpectedly when HTML is formatted with indentation, as the whitespace between tags counts as a text node. HTML-minifying tools or careful template authoring can address this.\nComments inside an element do not prevent it from matching :empty. The pseudo-class is most reliable for elements whose emptiness is controlled programmatically, such as badge counts or dynamically populated containers.\n","permalink":"https://www.webstf.nl/baseline/2015/#empty","tags":null,"title":":empty"},{"categories":null,"content":"The :lang() pseudo-class matches elements based on the language they are in, as determined by the lang attribute or inherited language information. It allows language-specific styles to be applied without class names or additional markup.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 :lang(ar) { direction: rtl; font-family: \u0026#39;Noto Sans Arabic\u0026#39;, sans-serif; } :lang(ja) { font-family: \u0026#39;Noto Sans JP\u0026#39;, sans-serif; line-height: 1.8; } :lang(de) q { quotes: \u0026#39;„\u0026#39; \u0026#39;\u0026#34;\u0026#39; \u0026#39;‚\u0026#39; \u0026#39;\u0026#39;\u0026#39;; } :lang(fr) q { quotes: \u0026#39;«\\00A0\u0026#39; \u0026#39;\\00A0»\u0026#39; \u0026#39;‹\\00A0\u0026#39; \u0026#39;\\00A0›\u0026#39;; } :lang() matches based on the element\u0026rsquo;s language tag, including inheritance — an element inside a \u0026lt;div lang=\u0026quot;fr\u0026quot;\u0026gt; matches :lang(fr) even without its own lang attribute. This is more reliable than using an attribute selector like [lang=\u0026quot;fr\u0026quot;], which only matches elements with the attribute explicitly present.\nThe selector accepts language range matching, so :lang(zh) matches elements in any Chinese variant (zh-Hans, zh-Hant, zh-TW, etc.). This makes it practical for applying broad typographic rules to a language family without needing to enumerate every subtag.\n:lang() is especially useful for multilingual documents where different sections need different quotation marks, fonts, text direction, or spacing.\n","permalink":"https://www.webstf.nl/baseline/2015/#lang","tags":null,"title":":lang()"},{"categories":null,"content":"The :nth-child() pseudo-class matches elements based on their position among all siblings within a parent, regardless of element type. It uses an An+B formula where A is the step size, B is the starting offset, and n counts from zero upward.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* Alternate row colors in a list */ li:nth-child(odd) { background: #f9f9f9; } li:nth-child(even) { background: white; } /* Every third item starting from the third */ .grid-item:nth-child(3n) { border-right: none; } /* First four items only */ .item:nth-child(-n+4) { font-weight: bold; } The keywords odd and even are shorthand for 2n+1 and 2n. Negative values of A count backwards, so -n+4 matches the first four elements (positions 4, 3, 2, 1 as n goes 0, 1, 2, 3).\nModern browsers support an optional selector argument inside :nth-child(), allowing you to filter by element type while still counting all siblings for the position:\n1 2 3 4 /* The second paragraph, counting only p elements */ :nth-child(2 of p) { margin-top: 2rem; } Related pseudo-classes include :first-child, :last-child, :nth-last-child() (counts from the end), and :only-child (matches when there is exactly one child). Together these give precise positional control over any element in a sibling group.\n","permalink":"https://www.webstf.nl/baseline/2015/#nth-child","tags":null,"title":":nth-child()"},{"categories":null,"content":"The :nth-of-type() pseudo-class matches elements based on their position among siblings of the same element type. Unlike :nth-child(), which counts all sibling elements regardless of type, :nth-of-type() counts only siblings that share the same tag name.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* Every other paragraph */ p:nth-of-type(odd) { background: #f9f9f9; } /* Every third image */ img:nth-of-type(3n) { border: 2px solid var(--color-accent); } /* Second h2 on the page */ h2:nth-of-type(2) { margin-top: 3rem; } The argument follows the same An+B formula used by :nth-child(): n is an integer counter starting at 0, A is the step, and B is the offset. The keywords odd and even are shorthand for 2n+1 and 2n respectively.\nThe family of related pseudo-classes includes :first-of-type, :last-of-type, and :nth-last-of-type(). The latter counts from the end rather than the start, so :nth-last-of-type(1) is equivalent to :last-of-type.\n:nth-of-type() is particularly useful when a container mixes different element types and you need to target specific elements of one type by position, without being affected by the positions of other element types in between.\n","permalink":"https://www.webstf.nl/baseline/2015/#nth-of-type","tags":null,"title":":nth-of-type() pseudo-classes"},{"categories":null,"content":"The :root pseudo-class matches the root element of the document — in HTML, this is always the \u0026lt;html\u0026gt; element. It is equivalent to the html type selector but has higher specificity, and is the conventional place to declare CSS custom properties (variables) that need to be available throughout the entire document.\n1 2 3 4 5 6 7 :root { --color-primary: oklch(55% 0.2 250); --color-text: #1a1a1a; --font-size-base: 1rem; --spacing-unit: 0.25rem; --border-radius: 4px; } Declaring custom properties on :root makes them inherited by all elements, effectively creating global CSS variables. This is the foundation of most CSS design token systems — a single location to define values that are then referenced throughout the stylesheet with var().\n:root is also used for global opt-ins to new CSS behaviours. For example:\n1 2 3 :root { interpolate-size: allow-keywords; } This sets the opt-in once at the document level, enabling intrinsic-size animations for all elements on the page. Similarly, color-scheme and view-transition-name are often set on :root to apply document-wide behaviour.\n","permalink":"https://www.webstf.nl/baseline/2015/#root","tags":null,"title":":root"},{"categories":null,"content":"The :target pseudo-class matches an element whose id matches the fragment identifier in the current URL — the part after the #. When a user navigates to page.html#section-2, the element with id=\u0026quot;section-2\u0026quot; matches :target.\n1 2 3 4 5 6 7 8 9 10 11 12 13 :target { scroll-margin-top: 5rem; /* Account for sticky header */ } .section:target { background: oklch(97% 0.02 250); outline: 2px solid var(--color-primary); border-radius: 4px; } :target h2 { color: var(--color-primary); } :target is commonly used to highlight the section a user has navigated to via an anchor link, providing visual feedback that the correct destination has been reached. This is especially useful in long documents with a table of contents.\nA creative application is using :target for pure-CSS interactive components — tab panels, modals, and accordions — by linking to different fragment identifiers and styling the targeted element as \u0026ldquo;active\u0026rdquo;. While this technique requires no JavaScript, it does modify the browser history and URL on each interaction, which can affect the back button behaviour and accessibility, so it should be used with those trade-offs in mind.\nscroll-margin-top on :target is a practical addition that prevents a sticky header from obscuring the targeted element when the browser scrolls to it.\n","permalink":"https://www.webstf.nl/baseline/2015/#target","tags":null,"title":":target"},{"categories":null,"content":"The @charset at-rule specifies the character encoding of a CSS stylesheet. When present, it must be the very first statement in the file — before any other rules, comments, or whitespace — and it must use double quotes around the encoding name.\n1 2 3 4 5 @charset \u0026#34;UTF-8\u0026#34;; body { font-family: sans-serif; } In practice, @charset \u0026quot;UTF-8\u0026quot; is the only value you will encounter in modern web development. UTF-8 encodes the full Unicode character set and is the standard encoding for all web content.\nThe rule exists primarily for cases where the stylesheet contains non-ASCII characters directly in the source — for example, string values, content property text, or comments written in languages that use characters outside the basic Latin range. When the server sends a stylesheet with an explicit Content-Type: text/css; charset=UTF-8 HTTP header, @charset is redundant, as the HTTP header takes precedence. Similarly, a UTF-8 byte order mark (BOM) at the start of the file also takes priority.\nFor most workflows today — where files are saved as UTF-8 and served with correct headers — @charset is not strictly necessary, but including it causes no harm and can prevent encoding issues in edge cases where the HTTP header is absent or incorrect.\n","permalink":"https://www.webstf.nl/baseline/2015/#charset","tags":null,"title":"@charset"},{"categories":null,"content":"The @import at-rule imports an external stylesheet into the current one. It must appear before any other rules in a stylesheet (except @charset and @layer declarations), and causes the browser to fetch and apply the referenced file as if its contents were inserted at that point.\n1 2 3 @import url(\u0026#39;reset.css\u0026#39;); @import url(\u0026#39;typography.css\u0026#39;); @import url(\u0026#39;https://fonts.googleapis.com/css2?family=Inter:wght@400;600\u0026#39;); @import supports conditional loading via media queries and supports() conditions:\n1 2 3 @import url(\u0026#39;print.css\u0026#39;) print; @import url(\u0026#39;wide.css\u0026#39;) screen and (min-width: 1200px); @import url(\u0026#39;grid.css\u0026#39;) supports(display: grid); @import also supports the layer() function, which wraps the imported stylesheet in a named cascade layer — a clean way to integrate third-party styles at a controlled priority level:\n1 @import url(\u0026#39;third-party.css\u0026#39;) layer(vendor); One performance consideration: @import is blocking and sequential. Each imported file must be fetched before the browser can discover further imports within it, creating a chain that delays rendering. For production use, tools that bundle CSS files together are generally preferable to relying on @import at runtime.\n","permalink":"https://www.webstf.nl/baseline/2015/#import","tags":null,"title":"@import"},{"categories":null,"content":"The @namespace at-rule declares an XML namespace to be used in CSS selectors. It allows stylesheets to target elements belonging to a specific XML namespace, which is primarily relevant in mixed-namespace documents such as XHTML pages that embed SVG or MathML content.\n1 2 3 4 5 6 7 8 9 10 11 12 @namespace url(\u0026#39;http://www.w3.org/1999/xhtml\u0026#39;); @namespace svg url(\u0026#39;http://www.w3.org/2000/svg\u0026#39;); /* Targets only XHTML \u0026#39;a\u0026#39; elements */ a { color: blue; } /* Targets only SVG \u0026#39;a\u0026#39; elements */ svg|a { color: inherit; } A @namespace declaration with no prefix sets the default namespace — type selectors without a prefix will only match elements in that namespace. A prefixed declaration associates a prefix with a namespace URI, and that prefix can then be used in selectors with the | separator syntax.\n@namespace rules must appear after any @charset and @import rules, and before any other at-rules or style rules. In practice, @namespace is rarely needed in standard HTML documents, as HTML parsers handle element namespaces automatically. Its relevance is primarily in SVG stylesheets or XHTML documents where multiple XML namespaces coexist and need to be selectively targeted.\n","permalink":"https://www.webstf.nl/baseline/2015/#namespace","tags":null,"title":"@namespace"},{"categories":null,"content":"The @supports at-rule applies a block of CSS rules conditionally, based on whether the browser supports a specified CSS feature. It is the primary tool for progressive enhancement — layering modern CSS on top of a working baseline without breaking browsers that do not yet support the feature.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 @supports (display: grid) { .layout { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } } @supports not (display: grid) { .layout { display: flex; flex-wrap: wrap; } } Conditions can test any property-value pair. The not, and, and or operators allow compound conditions:\n1 2 3 4 5 6 7 8 9 10 @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { .glass { backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); } } @supports (display: grid) and (gap: 1rem) { /* Both must be supported */ } The selector() function tests whether a selector syntax is understood by the browser:\n1 2 3 4 5 @supports selector(:has(\u0026gt; img)) { .card:has(\u0026gt; img) { padding: 0; } } The font-tech() and font-format() functions test font technology and format support respectively, useful inside @font-face rules.\n@supports queries can also be used in JavaScript via CSS.supports('display', 'grid') or CSS.supports('(display: grid)'), returning a boolean without needing to parse a stylesheet.\n","permalink":"https://www.webstf.nl/baseline/2015/#supports","tags":null,"title":"@supports"},{"categories":null,"content":"The \u0026lt;ol\u0026gt;, \u0026lt;ul\u0026gt;, and \u0026lt;li\u0026gt; HTML elements have default browser styles that CSS can override or build on. \u0026lt;ul\u0026gt; renders with bullet markers by default, \u0026lt;ol\u0026gt; with decimal counters, and both have default padding that creates the indented appearance for their list items.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 /* Reset default list styles */ ul, ol { list-style: none; padding: 0; margin: 0; } /* Custom unordered list */ ul.checklist li { padding-left: 1.5rem; position: relative; } ul.checklist li::before { content: \u0026#39;✓\u0026#39;; position: absolute; left: 0; color: green; } /* Styled ordered list */ ol.steps { list-style: none; counter-reset: steps; } ol.steps li { counter-increment: steps; padding-left: 3rem; position: relative; } ol.steps li::before { content: counter(steps); position: absolute; left: 0; width: 2rem; height: 2rem; background: var(--color-primary); color: white; border-radius: 50%; display: grid; place-items: center; } The ::marker pseudo-element provides a more direct way to style the default list markers — changing their color, size, or content — without replacing them with ::before generated content. list-style-type can accept a string value for a simple custom marker character, or reference a @counter-style rule for more complex marker systems.\nRemoving list styles with list-style: none also removes the semantic list role in some browsers when combined with display: contents or certain accessibility tree configurations, so ARIA roles may be needed when heavily restyling navigation lists.\n","permalink":"https://www.webstf.nl/baseline/2015/#list-elements","tags":null,"title":"\u003col\u003e, \u003cul\u003e and \u003cli\u003e"},{"categories":null,"content":"CSS 2D transforms move, rotate, scale, and skew elements visually without affecting the document layout. The transformed element occupies its original space in the flow — surrounding elements do not rearrange — making transforms ideal for animation and visual effects.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .card:hover { transform: translateY(-4px) scale(1.02); } .icon { transform: rotate(45deg); } .skewed-banner { transform: skewX(-8deg); } .mirrored { transform: scaleX(-1); } The main 2D transform functions are translate(x, y), translateX(), translateY(), rotate(), scale(x, y), scaleX(), scaleY(), skew(x, y), skewX(), skewY(), and matrix(). Multiple functions can be chained in a single transform declaration — they are applied right to left.\nThe transform-origin property sets the point around which transformations are applied. It defaults to the element\u0026rsquo;s centre (50% 50%). Changing it alters how rotations and scales are anchored:\n1 2 3 4 .rotating-hand { transform-origin: bottom center; transform: rotate(30deg); } Individual transform properties — translate, rotate, and scale — are now available as standalone properties, making it easier to animate each independently without interfering with the others in the transform shorthand:\n1 2 3 4 5 6 .element { translate: 0 0; rotate: 0deg; scale: 1; transition: translate 0.3s ease, scale 0.3s ease; } ","permalink":"https://www.webstf.nl/baseline/2015/#transforms2d","tags":null,"title":"2D transforms"},{"categories":null,"content":"Absolute positioning removes an element from the normal document flow and places it at a specific position relative to its nearest positioned ancestor — that is, the closest ancestor with a position value other than static. If no such ancestor exists, the element is positioned relative to the initial containing block (essentially the viewport).\n1 2 3 4 5 6 7 8 9 .parent { position: relative; } .child { position: absolute; top: 1rem; right: 1rem; } Because an absolutely positioned element is taken out of flow, it does not occupy space in the layout — surrounding elements behave as if it is not there. This makes it well suited for overlays, badges, tooltips, and decorative elements that need to be placed precisely without affecting the layout of other content.\nThe top, right, bottom, and left properties control the offset from the edges of the containing block. If none are specified, the element stays in its natural position in the text flow but is still removed from the layout\u0026rsquo;s influence on other elements.\nAn absolutely positioned element\u0026rsquo;s size defaults to shrink-to-fit (wrapping its content), but can be stretched to fill its containing block by setting opposing properties simultaneously, such as left: 0 and right: 0.\n","permalink":"https://www.webstf.nl/baseline/2015/#absolute-positioning","tags":null,"title":"Absolute positioning"},{"categories":null,"content":"CSS animations allow elements to transition through a sequence of styles over time, defined by @keyframes rules and controlled by a set of animation-* properties. Unlike transitions, animations can run automatically without a state change trigger, loop indefinitely, and move through multiple intermediate steps.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 @keyframes fade-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } } .alert { animation: fade-in 0.3s ease forwards; } The animation shorthand combines animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode, and animation-play-state.\n1 2 3 4 5 6 7 8 9 10 11 .spinner { animation: rotate 1s linear infinite; } @keyframes rotate { to { transform: rotate(360deg); } } .pulse { animation: pulse 2s ease-in-out 0.5s 3 alternate both; } animation-fill-mode: forwards keeps the final keyframe state applied after the animation ends. backwards applies the first keyframe during the delay period. both does both. animation-direction: alternate reverses the animation on each odd iteration, useful for back-and-forth effects.\nMultiple animations can be applied to a single element as a comma-separated list. Respecting prefers-reduced-motion by wrapping decorative animations in a media query is an important accessibility consideration.\n","permalink":"https://www.webstf.nl/baseline/2015/#animations-css","tags":null,"title":"Animations (CSS)"},{"categories":null,"content":"The background shorthand sets all background-related properties in a single declaration: background-color, background-image, background-repeat, background-position, background-size, background-origin, background-clip, and background-attachment.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .hero { background: url(\u0026#39;image.jpg\u0026#39;) center / cover no-repeat #1a1a1a; } .striped { background: repeating-linear-gradient( 45deg, #f0f0f0, #f0f0f0 10px, white 10px, white 20px ); } When background-size is included, it must immediately follow background-position separated by a slash: center / cover. background-color can only be set on the last (or only) layer and always sits beneath all image layers.\nMultiple background layers can be stacked using a comma-separated list. Each layer corresponds to a separate background-image, with individual position, size, repeat, origin, and clip settings. The first layer in the list is rendered on top.\n1 2 3 4 5 6 .layered { background: url(\u0026#39;overlay.png\u0026#39;) top right no-repeat, url(\u0026#39;texture.png\u0026#39;) repeat, #f5f0eb; } As with other shorthand properties, any longhand not explicitly set in the background shorthand is reset to its initial value, which can unintentionally override earlier declarations. Using the individual longhand properties provides more predictable cascade behaviour when building on existing styles.\n","permalink":"https://www.webstf.nl/baseline/2015/#background","tags":null,"title":"background"},{"categories":null,"content":"The background-attachment property controls whether a background image scrolls with the page or remains fixed relative to the viewport. It accepts three values: scroll, fixed, and local.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .hero { background-image: url(\u0026#39;landscape.jpg\u0026#39;); background-attachment: fixed; /* Parallax effect */ background-size: cover; background-position: center; } .card { background-attachment: scroll; /* Default: moves with element */ } .scrollable-box { overflow: auto; background-image: url(\u0026#39;pattern.png\u0026#39;); background-attachment: local; /* Scrolls with the element\u0026#39;s content */ } scroll (the default) fixes the background relative to the element itself — the background moves with the element as the page scrolls, but does not scroll with the element\u0026rsquo;s own overflow content. fixed fixes the background relative to the viewport, creating a parallax-like effect where the background stays stationary as the page scrolls. local fixes the background relative to the element\u0026rsquo;s content, so it scrolls along when the element\u0026rsquo;s own overflow is scrolled.\nbackground-attachment: fixed creates a stacking context and can interact unexpectedly with background-size: cover on mobile devices, where fixed backgrounds are often treated as scroll for performance reasons. For reliable cross-device parallax effects, CSS transform-based or scroll-driven animation approaches are generally more predictable.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-attachment","tags":null,"title":"background-attachment"},{"categories":null,"content":"The background-clip property controls how far the background — color and image — extends within an element\u0026rsquo;s box. It clips the background to a specific area: the border box, padding box, content box, or just the text.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .border-box { background-clip: border-box; /* Default: extends under the border */ } .padding-box { background-clip: padding-box; /* Stops at the inner edge of the border */ } .content-box { background-clip: content-box; /* Confined to the content area only */ } .gradient-text { background: linear-gradient(135deg, #f093fb, #f5576c); background-clip: text; -webkit-background-clip: text; color: transparent; } The text value clips the background to the foreground text, allowing gradients and images to show through letterforms. Setting color: transparent makes the text itself invisible so only the clipped background is visible, producing the gradient text effect. Both the prefixed -webkit-background-clip: text and the unprefixed form are typically included for compatibility.\nThe newer border-area value clips the background to the border region only — excluding the padding and content — making it straightforward to paint gradients directly onto borders without pseudo-elements or border-image.\nWhen multiple background layers are used, background-clip accepts comma-separated values to configure each layer independently.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-clip","tags":null,"title":"background-clip"},{"categories":null,"content":"The background-color property sets the background color of an element, filling its content area, padding, and (by default) the area behind its border. It accepts any valid CSS color value, including named colors, hex values, rgb(), hsl(), oklch(), and the transparent keyword.\n1 2 3 4 5 6 7 8 9 10 11 .card { background-color: #f5f5f5; } .highlight { background-color: rgb(255 235 59 / 0.4); } .glass { background-color: transparent; } background-color is rendered beneath any background-image set on the same element, so it acts as a fallback when an image fails to load, and as the base layer visible through transparent areas of the image.\nUnlike background-image, background-color cannot be layered — only one background color applies per element. However, it interacts with background-clip, which controls how far the color extends: border-box (default), padding-box, or content-box. Setting background-color to transparent is equivalent to its initial value and is a common way to explicitly remove an inherited or default background.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-color","tags":null,"title":"background-color"},{"categories":null,"content":"The background-image property sets one or more images as the background of an element. It accepts url() references to image files, CSS gradient functions, or none. Multiple images can be layered by providing a comma-separated list, with the first image rendering on top.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .card { background-image: url(\u0026#39;pattern.svg\u0026#39;); } .gradient-header { background-image: linear-gradient(135deg, #667eea, #764ba2); } .layered { background-image: url(\u0026#39;icon.png\u0026#39;), radial-gradient(circle at top right, #f093fb, transparent); } background-image is always rendered on top of background-color, which acts as a fallback when the image fails to load and shows through transparent areas of the image.\nThe full range of CSS gradient types — linear-gradient(), radial-gradient(), conic-gradient(), and their repeating-* variants — are valid image values. This makes it possible to create geometric patterns, color transitions, and decorative effects without any image files.\nbackground-image works in conjunction with background-repeat (controlling tiling), background-position (controlling placement), background-size (controlling dimensions), and background-clip (controlling which area the image covers). Each of these properties also accepts comma-separated values to configure each image layer independently.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-image","tags":null,"title":"background-image"},{"categories":null,"content":"The background-origin property determines the reference area used for positioning and sizing a background image. It defines the origin point from which background-position is calculated and the area that percentage values in background-size are relative to.\n1 2 3 4 5 6 7 8 9 10 11 .border-box-origin { background-origin: border-box; /* Position relative to border edge */ } .padding-box-origin { background-origin: padding-box; /* Default: relative to padding edge */ } .content-box-origin { background-origin: content-box; /* Position relative to content edge */ } The default value is padding-box, which means background-position: 0 0 places the image at the top-left corner of the padding area — inside the border. With border-box, 0 0 positions the image at the very top-left corner of the border itself. With content-box, 0 0 aligns to the top-left of the content area, excluding padding.\nbackground-origin is ignored when background-attachment is set to fixed, as fixed backgrounds are positioned relative to the viewport regardless.\nThe difference between background-origin and background-clip is subtle but important: background-origin determines where the image is positioned and sized from, while background-clip determines where the background is visible up to. They are independent — you can position a background relative to the border box but clip it to the padding box, for example.\nWhen using multiple background layers, background-origin accepts comma-separated values corresponding to each layer.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-origin","tags":null,"title":"background-origin"},{"categories":null,"content":"The background-position property sets the starting position of a background image within its background positioning area (determined by background-origin). It accepts keywords, lengths, and percentages, and can be defined as a single value or a pair of values for horizontal and vertical axes.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .hero { background-image: url(\u0026#39;photo.jpg\u0026#39;); background-position: center; /* Centre on both axes */ } .top-right { background-position: top right; } .offset { background-position: 20px 40px; /* 20px from left, 40px from top */ } .percentage { background-position: 75% 25%; } When a single keyword or value is given, the other axis defaults to center. The keyword pairs left, center, right (horizontal) and top, center, bottom (vertical) can be combined in any order when both are named keywords.\nA four-value syntax allows offset from any edge:\n1 2 3 4 .edge-offset { /* 20px from the right edge, 30px from the bottom */ background-position: right 20px bottom 30px; } Percentage values are relative to the difference between the background positioning area and the background image size — 50% on both axes is equivalent to center center, meaning the image\u0026rsquo;s centre aligns with the area\u0026rsquo;s centre.\nWhen multiple background layers are used, background-position accepts comma-separated values, one per layer.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-position","tags":null,"title":"background-position"},{"categories":null,"content":"The background-size property sets the dimensions of a background image. It accepts length values, percentages, and the keywords auto, cover, and contain.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .hero { background-image: url(\u0026#39;photo.jpg\u0026#39;); background-size: cover; /* Fill the container, cropping if necessary */ background-position: center; } .logo { background-image: url(\u0026#39;logo.svg\u0026#39;); background-size: contain; /* Fit entirely within the container */ background-repeat: no-repeat; } .pattern { background-image: url(\u0026#39;tile.png\u0026#39;); background-size: 40px 40px; /* Explicit dimensions */ } .wide { background-size: 100% auto; /* Full width, auto height */ } cover scales the image to be as large as possible while ensuring it covers the entire background area — some parts of the image may be cropped if the aspect ratio differs from the container. contain scales the image to fit entirely within the background area without cropping, which may leave uncovered space (filled by background-color or tiling if background-repeat allows it).\nTwo values set width and height independently. A single value sets the width, with height defaulting to auto to preserve the image\u0026rsquo;s aspect ratio. Percentage values are relative to the background positioning area (determined by background-origin).\nWhen using multiple background layers, background-size accepts a comma-separated list of values, with each value corresponding to a layer in background-image.\n","permalink":"https://www.webstf.nl/baseline/2015/#background-size","tags":null,"title":"background-size"},{"categories":null,"content":"The baseline-shift property moves an inline element up or down relative to the dominant baseline of its parent. It is the CSS equivalent of the SVG baseline-shift presentation attribute, and is primarily used in SVG text to produce superscript and subscript effects without changing font size.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tspan.superscript { baseline-shift: super; } tspan.subscript { baseline-shift: sub; } tspan.raised { baseline-shift: 0.4em; } tspan.lowered { baseline-shift: -0.3em; } The super and sub keywords shift the element to a typical superscript or subscript position relative to the parent\u0026rsquo;s baseline. Length and percentage values provide precise control over the shift amount — positive values move the element up, negative values move it down.\nIn HTML, vertical-align with super and sub values, or with explicit length offsets, serves the same purpose for inline elements. baseline-shift is specifically relevant when authoring SVG text or working in contexts where SVG\u0026rsquo;s typographic model applies. The two properties address the same visual intent through different specifications: vertical-align for HTML inline layout, baseline-shift for SVG text layout.\n","permalink":"https://www.webstf.nl/baseline/2015/#baseline-shift","tags":null,"title":"baseline-shift"},{"categories":null,"content":"The border-radius shorthand property rounds the corners of an element\u0026rsquo;s outer border edge. It accepts one to four values following the standard shorthand pattern for top-left, top-right, bottom-right, and bottom-left corners. A single value rounds all four corners equally.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .card { border-radius: 8px; } .pill { border-radius: 999px; /* Large value produces a fully rounded pill shape */ } .asymmetric { border-radius: 4px 16px; /* top-left/bottom-right, top-right/bottom-left */ } .circle { width: 4rem; height: 4rem; border-radius: 50%; } Each corner can independently have an elliptical radius by providing two values separated by a slash — the first for the horizontal radius and the second for the vertical:\n1 2 3 4 5 6 7 .elliptical { border-radius: 50% / 30%; /* Horizontal / vertical radii */ } .leaf { border-radius: 0 100% 0 100%; /* Alternating corners for organic shapes */ } border-radius applies to the element\u0026rsquo;s background, border, and box-shadow — but not to child elements or content, which can overflow the rounded corners unless overflow: hidden is set on the element.\nThe longhand properties — border-top-left-radius, border-top-right-radius, border-bottom-right-radius, and border-bottom-left-radius — each accept the two-value horizontal/vertical syntax individually for precise control over each corner.\n","permalink":"https://www.webstf.nl/baseline/2015/#border-radius","tags":null,"title":"border-radius"},{"categories":null,"content":"The border shorthand property sets the width, style, and color of an element\u0026rsquo;s border in a single declaration. Each of these components can also be set individually with border-width, border-style, and border-color, or per-side with properties like border-top, border-right, border-bottom, and border-left.\n1 2 3 4 5 6 7 8 9 10 11 12 .box { border: 2px solid #333; } .subtle { border-bottom: 1px dashed #ccc; } .rounded { border: 1px solid #e0e0e0; border-radius: 8px; } border-style accepts values including solid, dashed, dotted, double, groove, ridge, inset, and outset. A border must have a non-none style to be visible, regardless of width and color.\nborder-radius controls the rounding of corners and works independently of the border itself — you can have rounded corners on an element with no border, and the rounding will apply to the background and any outline. For more decorative borders — gradients, images, or complex shapes — border-image provides an alternative to a solid colored border.\n","permalink":"https://www.webstf.nl/baseline/2015/#borders","tags":null,"title":"Borders"},{"categories":null,"content":"The box-shadow property adds one or more shadows to an element\u0026rsquo;s box. Each shadow is defined by a horizontal offset, a vertical offset, an optional blur radius, an optional spread radius, and a color. Multiple shadows are stacked in a comma-separated list, with the first shadow appearing on top.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .card { box-shadow: 0 2px 8px rgb(0 0 0 / 0.12); } .elevated { box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 4px 16px rgb(0 0 0 / 0.12); } .inset-shadow { box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.15); } .glow { box-shadow: 0 0 16px 4px oklch(70% 0.3 200 / 0.6); } The horizontal and vertical offsets move the shadow from the element\u0026rsquo;s position — positive values go right and down. The blur radius softens the edge; a value of 0 produces a hard-edged shadow. The spread radius expands or contracts the shadow beyond the element\u0026rsquo;s dimensions — positive values grow it, negative values shrink it.\nThe inset keyword moves the shadow inside the element\u0026rsquo;s border, creating a recessed or inner-glow effect.\nLayering multiple shadows with slightly different offsets, blurs, and opacities produces more natural-looking elevation effects than a single shadow. box-shadow respects border-radius — rounded corners produce rounded shadows. Unlike filter: drop-shadow(), box-shadow always follows the rectangular box of the element and does not follow the shape of transparent PNG images or SVG content.\n","permalink":"https://www.webstf.nl/baseline/2015/#box-shadow","tags":null,"title":"box-shadow"},{"categories":null,"content":"The box-sizing property controls how an element\u0026rsquo;s total width and height are calculated — specifically whether padding and border are included within the declared dimensions or added outside them.\nThe default value, content-box, sizes the element to its content area only. Padding and border are added on top of the declared width and height, making the rendered element larger than its specified dimensions:\n1 2 3 4 5 6 7 .content-box { box-sizing: content-box; /* Default */ width: 300px; padding: 20px; border: 2px solid; /* Rendered width: 300 + 40 + 4 = 344px */ } border-box includes padding and border within the declared dimensions, so the element renders at exactly the specified width and height:\n1 2 3 4 5 6 7 .border-box { box-sizing: border-box; width: 300px; padding: 20px; border: 2px solid; /* Rendered width: exactly 300px */ } border-box is almost universally considered more intuitive and is applied globally in most modern projects via a reset:\n1 2 3 *, *::before, *::after { box-sizing: border-box; } This reset makes sizing predictable throughout a codebase — a width: 100% element with padding will not overflow its container. It is included in virtually all CSS resets and design system base stylesheets.\n","permalink":"https://www.webstf.nl/baseline/2015/#box-sizing","tags":null,"title":"box-sizing"},{"categories":null,"content":"The calc() function performs mathematical calculations within CSS property values, allowing different unit types to be mixed in a single expression. It accepts addition (+), subtraction (-), multiplication (*), and division (/).\n1 2 3 4 5 6 7 8 9 10 11 12 .sidebar { width: calc(100% - 2rem); } .element { padding: calc(1rem + 4px); font-size: calc(1rem * 1.25); } .centered { left: calc(50% - 150px); /* Centre a 300px element */ } The most powerful aspect of calc() is mixing units — 100% - 2rem is not possible with any single unit, but calc() resolves both sides to their concrete pixel values at render time and computes the result. Whitespace around + and - operators is required; * and / do not require it but it is recommended for readability.\ncalc() can reference CSS custom properties:\n1 2 3 4 5 6 7 :root { --nav-height: 60px; } .content { height: calc(100vh - var(--nav-height)); } Nesting calc() inside itself is valid but redundant — the inner calc() can be omitted. More specialised math functions build on the same foundation: min(), max(), and clamp() for range constraints; round(), mod(), rem(), sin(), cos(), tan(), pow(), sqrt(), log(), and exp() for advanced calculations. calc-size() extends the concept to intrinsic size keywords like auto and fit-content.\n","permalink":"https://www.webstf.nl/baseline/2015/#calc","tags":null,"title":"calc()"},{"categories":null,"content":"The ch unit represents the width of the \u0026ldquo;0\u0026rdquo; (zero) character in the current font. It provides a character-width-relative unit, making it particularly useful for sizing text inputs, code blocks, and containers where a meaningful width expressed in terms of characters is more intuitive than an abstract length.\n1 2 3 4 5 6 7 8 9 10 11 input[type=\u0026#34;text\u0026#34;] { width: 30ch; /* Room for approximately 30 characters */ } .code-block { max-width: 80ch; /* Classic terminal line length */ } .narrow-column { width: 45ch; } Because ch is based on the \u0026ldquo;0\u0026rdquo; character — which in proportional fonts is typically close to the average character width — a width of 60ch gives a rough approximation of 60 characters of text, though actual character count varies with the content. In monospace fonts, \u0026ldquo;0\u0026rdquo; has the same width as every other character, so ch is perfectly accurate for character counting in code contexts.\nch responds to changes in font-size and font-family, scaling appropriately as the font changes. Its root-relative counterpart, rch, always references the root element\u0026rsquo;s font rather than the current element\u0026rsquo;s font.\n","permalink":"https://www.webstf.nl/baseline/2015/#ch","tags":null,"title":"ch unit"},{"categories":null,"content":"The color property sets the foreground color of an element\u0026rsquo;s text content and text decorations, and establishes the value of the currentcolor keyword for that element and its descendants. It accepts any valid CSS color value.\n1 2 3 4 5 6 7 8 9 10 11 body { color: #1a1a1a; } a { color: oklch(50% 0.2 250); } .muted { color: rgb(0 0 0 / 0.5); } color is inherited, so setting it on a parent element cascades down to all children unless overridden. This makes it efficient for establishing a base text color on body or a component root.\nThe currentcolor keyword is one of color\u0026rsquo;s most useful side effects — it exposes the computed text color as a reusable value for other properties. Setting border-color: currentcolor or fill: currentcolor on an SVG icon, for example, means those values automatically track the element\u0026rsquo;s text color without needing to repeat it.\nCSS now supports a wide range of color spaces beyond the traditional sRGB model, including oklch, display-p3, and others, accessible through modern color functions for more precise and perceptually uniform color choices.\n","permalink":"https://www.webstf.nl/baseline/2015/#color","tags":null,"title":"Color"},{"categories":null,"content":"The content property specifies the content to be rendered by a ::before or ::after pseudo-element. It is what makes generated content possible — inserting text, images, counters, or attribute values into the document without modifying the HTML.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .required::after { content: \u0026#39; *\u0026#39;; color: red; } a[href^=\u0026#34;https\u0026#34;]::before { content: url(\u0026#39;/icons/external.svg\u0026#39;); } .chapter::before { content: \u0026#39;Chapter \u0026#39; counter(chapter) \u0026#39;: \u0026#39;; counter-increment: chapter; } content accepts a string, a url() pointing to an image, counter() and counters() for CSS counters, attr() to pull in an HTML attribute value, and special keywords like open-quote and close-quote. Multiple values can be concatenated by listing them with spaces.\nSetting content: none or content: normal on a pseudo-element suppresses it entirely, which is useful for resetting generated content in certain contexts.\nThe property also supports an accessibility alt text via the slash syntax — content: url('/icon.svg') / 'Home' — allowing generated images or symbols to carry a meaningful text alternative for assistive technologies.\n","permalink":"https://www.webstf.nl/baseline/2015/#content","tags":null,"title":"Content"},{"categories":null,"content":"CSS counters allow you to maintain and display automatically incrementing numbers in generated content, without JavaScript. They are defined and manipulated using counter-reset, counter-increment, and counter-set, and displayed using the counter() and counters() functions inside the content property.\n1 2 3 4 5 6 7 8 9 10 11 body { counter-reset: section; } h2 { counter-increment: section; } h2::before { content: counter(section) \u0026#39;. \u0026#39;; } counter-reset initialises a counter to a given value (default 0) on an element. counter-increment adds to the counter each time the element appears. counter() retrieves the current value for display.\nCounters can be nested using counters(), which outputs all levels of a counter separated by a specified string — useful for hierarchical numbering like \u0026ldquo;1.2.3\u0026rdquo; in outlines and legal documents:\n1 2 3 4 5 6 7 8 9 10 11 ol { counter-reset: item; } li { counter-increment: item; } li::marker { content: counters(item, \u0026#39;.\u0026#39;) \u0026#39; \u0026#39;; } The display style of a counter can be changed by passing a list style type as a second argument to counter(): counter(section, upper-roman) outputs Roman numerals. CSS counters work entirely in the browser without scripting and are most commonly used for numbered headings, figures, footnotes, and custom ordered list styles.\n","permalink":"https://www.webstf.nl/baseline/2015/#counters","tags":null,"title":"Counters (CSS)"},{"categories":null,"content":"The cubic-bezier() function defines a custom easing curve for CSS transitions and animations. It takes four numbers representing the x and y coordinates of two control points on a cubic Bézier curve, giving precise control over how a value accelerates and decelerates over the duration of an animation.\n1 2 3 4 5 6 7 8 9 10 11 .smooth { transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Material ease */ } .bounce-out { transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); } .sharp-in { transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1); } The four values are x1, y1, x2, y2, where (x1, y1) and (x2, y2) are the two control points. The x values must be between 0 and 1 (they represent time); the y values can exceed this range to produce overshoot and bounce effects.\nThe built-in keyword easing functions are shorthands for common cubic-bezier values: ease is cubic-bezier(0.25, 0.1, 0.25, 1), ease-in is cubic-bezier(0.42, 0, 1, 1), ease-out is cubic-bezier(0, 0, 0.58, 1), and ease-in-out is cubic-bezier(0.42, 0, 0.58, 1). linear is cubic-bezier(0, 0, 1, 1).\nBrowser DevTools in Chrome, Firefox, and Safari all include a visual cubic-bezier editor that lets you drag the control points and preview the curve in real time, making it straightforward to dial in the exact feel you want without manual calculation.\n","permalink":"https://www.webstf.nl/baseline/2015/#cubic-bezier-easing","tags":null,"title":"cubic-bezier() easing"},{"categories":null,"content":"currentColor is a CSS keyword that resolves to the computed value of the color property on the same element. It allows any property that accepts a color value to automatically inherit and reuse the element\u0026rsquo;s text color, without repeating the value.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .icon { color: var(--brand-color); border: 2px solid currentColor; /* Same as --brand-color */ box-shadow: 0 0 8px currentColor; } svg { fill: currentColor; /* SVG inherits the text color of its parent */ } .badge { color: #c0392b; background: transparent; border: 1.5px solid currentColor; /* Matches the red text */ } currentColor is particularly powerful with SVG icons inlined in HTML or used as background images via mask. Setting fill: currentColor on an SVG means the icon automatically adopts the text color of whatever element it is placed inside, making it trivial to create icons that respond to theme changes, hover states, and color overrides without any additional CSS.\nBecause color is an inherited property, currentColor also inherits — a child element using currentColor will pick up the color of its nearest ancestor that has one set, following the normal inheritance chain.\n","permalink":"https://www.webstf.nl/baseline/2015/#currentcolor","tags":null,"title":"currentColor"},{"categories":null,"content":"The cursor property sets the type of cursor shown when the mouse pointer is over an element. It communicates affordance — what the user can do with the element — and is an important part of interactive UI design.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .button { cursor: pointer; /* Hand cursor, signals clickability */ } .disabled { cursor: not-allowed; } .draggable { cursor: grab; } .draggable:active { cursor: grabbing; } .resizable { cursor: ew-resize; /* East-west resize arrow */ } .text-area { cursor: text; } The most commonly used keyword values include default (the standard arrow), pointer (pointing hand for links and buttons), text (I-beam for text), move (four-directional arrow), grab and grabbing (for draggable elements), not-allowed (blocked action), wait (loading), crosshair, and a full set of directional resize cursors (n-resize, ew-resize, nwse-resize, etc.).\nCustom cursors can be specified using url(), with fallback keywords:\n1 2 3 .custom { cursor: url(\u0026#39;cursor.png\u0026#39;) 8 8, pointer; } The two numbers after the URL are the cursor\u0026rsquo;s hotspot coordinates — the pixel offset of the active click point from the top-left of the image. A fallback keyword is required after any url() value.\ncursor: none hides the cursor entirely, which is occasionally used in games or immersive experiences that render their own custom cursor with JavaScript.\n","permalink":"https://www.webstf.nl/baseline/2015/#cursor","tags":null,"title":"Cursor styles"},{"categories":null,"content":"The display property defines how an element participates in its parent\u0026rsquo;s layout (its outer display type) and how it lays out its own children (its inner display type). It is one of the most fundamental properties in CSS.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .inline-element { display: inline; } .block-element { display: block; } .flex-container { display: flex; } .grid-container { display: grid; } .hidden { display: none; } The modern two-value syntax makes the dual nature of display explicit: display: block flex means the element is a block-level box on the outside (in normal flow) but establishes a flex formatting context for its children. Single keywords like flex and grid are shorthands for block flex and block grid respectively.\ndisplay: none removes an element from layout entirely — it takes up no space and is invisible to assistive technologies. display: contents is a special value that causes the element itself to generate no box, but its children are treated as if they were direct children of the element\u0026rsquo;s parent — useful for semantic wrapper elements that should not affect layout.\nChanging display is now animatable using transition-behavior: allow-discrete, enabling enter and exit transitions on elements that toggle between display: none and a visible state.\n","permalink":"https://www.webstf.nl/baseline/2015/#display","tags":null,"title":"Display"},{"categories":null,"content":"display: list-item makes any element behave like a list item — generating a principal block box for content and a separate marker box for the list bullet or number. This is the default display value for \u0026lt;li\u0026gt; elements, but it can be applied to any element to give it list-item behaviour without using a list element.\n1 2 3 4 5 6 7 8 9 10 .custom-list-item { display: list-item; list-style-type: disc; margin-left: 1.5rem; } dt { display: list-item; list-style-type: \u0026#39;→ \u0026#39;; } Once display: list-item is set, the full range of list-style properties apply: list-style-type for the marker character or counter style, list-style-position for whether the marker sits inside or outside the content box, and list-style-image for a custom image marker.\nThis is particularly useful when semantic list markup is not appropriate but a visual list treatment is desired, or when existing non-list elements need to be presented in a list-like format without restructuring the HTML. The marker box generated by display: list-item is also targetable via the ::marker pseudo-element for custom styling.\n","permalink":"https://www.webstf.nl/baseline/2015/#display-list-item","tags":null,"title":"display: list-item"},{"categories":null,"content":"The em unit is a relative length unit equal to the computed font-size of the element it is used on. If an element has a font-size of 16px, then 1em equals 16px for any property on that element — including margin, padding, width, and border-radius.\n1 2 3 4 5 6 7 8 9 10 .component { font-size: 1.125rem; padding: 0.75em 1em; /* Relative to 1.125rem, not the root */ border-radius: 0.25em; } h2 { font-size: 1.5rem; margin-bottom: 0.5em; /* Half of h2\u0026#39;s own font-size */ } When em is used on the font-size property itself, it is relative to the parent element\u0026rsquo;s font-size, not the element\u0026rsquo;s own. This is the source of the compounding effect em is known for — nested elements can unintentionally multiply their font sizes if em is used throughout.\nThis compounding behaviour is avoided by rem, which always references the root font size. em remains the preferred unit when you want spacing and sizing to scale proportionally with the local font size of a component — useful in design systems where components may be embedded at different sizes.\n","permalink":"https://www.webstf.nl/baseline/2015/#em-unit","tags":null,"title":"em unit"},{"categories":null,"content":"The ex unit represents the x-height of the current font — approximately the height of a lowercase \u0026ldquo;x\u0026rdquo;. Unlike em, which is based on the full em square, ex is tied to the visual height of lowercase letters, making it useful for fine typographic adjustments that need to feel optically proportional to the text rather than mathematically proportional to the font size.\n1 2 3 4 5 6 7 8 9 10 11 sup { /* Raise superscript relative to the x-height of the surrounding text */ vertical-align: 1ex; font-size: 0.75em; } .icon { /* Size icon to match lowercase letter height */ height: 1ex; width: 1ex; } The x-height varies significantly between typefaces — a font with tall lowercase letters will have a larger ex value than one with short lowercase letters, even at the same font-size. This makes ex more visually consistent across fonts than em for adjustments that need to align with the apparent height of lowercase text.\nIn practice, ex is most commonly used for vertical-align adjustments on inline elements like superscripts, subscripts, and inline icons, where alignment relative to the lowercase letter body produces better optical results than alignment relative to the baseline or cap height alone.\n","permalink":"https://www.webstf.nl/baseline/2015/#ex","tags":null,"title":"ex unit"},{"categories":null,"content":"Fixed positioning places an element relative to the viewport and keeps it there as the page scrolls. Like absolute positioning, it removes the element from the normal document flow, but its containing block is always the viewport rather than a positioned ancestor.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .sticky-nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; } .back-to-top { position: fixed; bottom: 2rem; right: 2rem; } Fixed elements are commonly used for navigation bars, cookie banners, floating action buttons, and chat widgets — anything that should remain visible regardless of scroll position.\nOne important exception to viewport-relative behaviour is when a fixed element\u0026rsquo;s ancestor has a transform, perspective, filter, or will-change property applied. In those cases, that ancestor becomes the containing block instead of the viewport, which can break the expected fixed behaviour. This is a frequent source of layout bugs when fixed headers or overlays are used alongside animated or transformed parent elements.\n","permalink":"https://www.webstf.nl/baseline/2015/#fixed-positioning","tags":null,"title":"Fixed positioning"},{"categories":null,"content":"Flexbox is a one-dimensional layout model that distributes space and aligns items along a single axis — either a row or a column. A flex container is created by setting display: flex or display: inline-flex on an element, making all direct children flex items.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .nav { display: flex; align-items: center; gap: 1rem; } .card-row { display: flex; flex-wrap: wrap; gap: 1.5rem; } .sidebar-layout { display: flex; gap: 2rem; } .sidebar { flex: 0 0 240px; /* Don\u0026#39;t grow, don\u0026#39;t shrink, fixed at 240px */ } .main { flex: 1; /* Grow to fill remaining space */ } The primary axis is set by flex-direction: row (default, left to right), row-reverse, column, or column-reverse. Items align along the cross axis using align-items (stretch by default) and along the main axis using justify-content.\nflex-wrap: wrap allows items to wrap onto multiple lines when they exceed the container width. gap sets spacing between items without affecting outer edges.\nEach flex item\u0026rsquo;s behaviour is controlled by three properties: flex-grow (how much the item grows relative to others), flex-shrink (how much it shrinks), and flex-basis (its initial size before growth or shrinkage). These combine into the flex shorthand: flex: 1 is equivalent to flex: 1 1 0, and flex: none to flex: 0 0 auto.\nIndividual items can override alignment with align-self, and their order can be changed with order without modifying the DOM — though this should be used with care as it separates visual and source order, which affects keyboard navigation and accessibility.\n","permalink":"https://www.webstf.nl/baseline/2015/#flexbox","tags":null,"title":"Flexbox"},{"categories":null,"content":"The float property shifts an element to the left or right side of its container, allowing inline content to wrap around it. A floated element is taken partially out of normal flow — it still participates in the block formatting context of its parent, but following inline content flows around it rather than being displaced by it.\n1 2 3 4 5 6 7 8 9 10 11 .figure { float: right; margin: 0 0 1rem 1.5rem; max-width: 40%; } .pull-quote { float: left; width: 35%; margin: 0 1.5rem 1rem 0; } The clear property on a subsequent element prevents it from sitting beside a float. Setting clear: left, clear: right, or clear: both pushes the element below any preceding floats in the specified direction.\n1 2 3 .article-footer { clear: both; } Floats were historically used for multi-column page layouts before flexbox and grid were available. Today their primary use case is the original one they were designed for: wrapping text around images and pull quotes in editorial content. For page layout, flexbox and grid are almost always more appropriate. The display: flow-root value on a container creates a new block formatting context that automatically contains its floated children, replacing the older clearfix hack.\n","permalink":"https://www.webstf.nl/baseline/2015/#float-clear","tags":null,"title":"float and clear"},{"categories":null,"content":"The font shorthand sets font-style, font-variant, font-weight, font-stretch (now font-width), font-size, line-height, and font-family in a single declaration. The order of values follows a strict syntax, and some components are optional.\n1 2 3 4 5 6 7 p { font: italic small-caps bold 1rem/1.6 \u0026#39;Georgia\u0026#39;, serif; } .label { font: 600 0.875rem/1 \u0026#39;Inter\u0026#39;, sans-serif; } The required components are font-size and font-family, which must always appear in that order, at the end of the declaration. line-height follows font-size separated by a slash. The optional components — font-style, font-variant, font-weight, and font-stretch — appear before font-size in any order.\nOne important caveat: any font-related longhand properties not explicitly set in the shorthand are reset to their initial values. This means using the font shorthand later in a stylesheet can silently undo earlier declarations of individual font properties.\nThe font shorthand also accepts system font keywords — caption, icon, menu, message-box, small-caption, and status-bar — which adopt the font used by the corresponding OS UI element, allowing web content to match the native system typography.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-shorthand","tags":null,"title":"Font shorthand"},{"categories":null,"content":"The font-family property specifies a prioritised list of font family names and generic family keywords. The browser works through the list from left to right, using the first font that is available on the system or loaded via @font-face.\n1 2 3 4 5 6 7 body { font-family: \u0026#39;Inter\u0026#39;, \u0026#39;Helvetica Neue\u0026#39;, Arial, sans-serif; } code { font-family: \u0026#39;Fira Code\u0026#39;, \u0026#39;Cascadia Code\u0026#39;, \u0026#39;Courier New\u0026#39;, monospace; } Font family names containing spaces must be quoted. Generic family keywords — serif, sans-serif, monospace, cursive, fantasy, and system-ui — do not need quotes and should always be included at the end of the list as an absolute fallback.\nCustom fonts are loaded using @font-face, which maps a family name to a font file. The font-display descriptor in @font-face controls how the browser handles the period before the font loads — whether it shows invisible text, a fallback font, or attempts to minimise layout shift.\nfont-family is an inherited property, so setting it on body or a component root cascades to all descendants unless overridden. Modern best practice often sets font-family using CSS custom properties or a design token system, making it straightforward to change typefaces globally or per-theme.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-family","tags":null,"title":"font-family"},{"categories":null,"content":"The font-language-override property controls which language-specific glyph variants a font uses, independently of the document\u0026rsquo;s lang attribute. Many OpenType fonts include alternate glyphs optimised for particular languages — the same Unicode character may be rendered differently in Serbian versus Russian, or in Traditional versus Simplified Chinese.\nNormally, the browser selects these variants based on the element\u0026rsquo;s language as set by lang. font-language-override lets you specify a different OpenType language system tag directly, overriding that automatic selection.\n1 2 3 4 5 6 7 8 .serbian-text { /* Use Serbian-specific glyph variants regardless of document lang */ font-language-override: \u0026#39;SRB\u0026#39;; } .romanian-text { font-language-override: \u0026#39;ROM\u0026#39;; } The value is an OpenType language system tag — a four-character string defined by the OpenType specification. The default value normal defers to the language inferred from the lang attribute.\nThis property is most useful when document-level language tagging does not match the language of a specific block of text, or when you need to mix glyph sets from different language variants within a single document.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-language-override","tags":null,"title":"font-language-override"},{"categories":null,"content":"The font-size property sets the size of the font for an element\u0026rsquo;s text. It accepts absolute lengths, relative lengths, percentages, and a set of keyword values. It is one of CSS\u0026rsquo;s most foundational properties and directly affects the computed values of em, ex, cap, ch, and other font-relative units on the same element.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 body { font-size: 1rem; /* 16px in most browsers by default */ } h1 { font-size: 2.5rem; } .small-print { font-size: 0.75em; /* 75% of parent\u0026#39;s font-size */ } .fluid { font-size: clamp(1rem, 2.5vw, 1.5rem); } rem (root em) is commonly preferred over em for font-size to avoid the compounding effect of nested em values. Percentage values behave identically to em — font-size: 150% means the same as font-size: 1.5em.\nKeyword values include absolute sizes (xx-small through xx-large) and relative sizes (smaller, larger) that step up or down relative to the parent. Using clamp() for font-size is a common approach to fluid typography, allowing the size to scale with the viewport while staying within a defined minimum and maximum range.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-size","tags":null,"title":"font-size"},{"categories":null,"content":"The font-style property selects between the normal, italic, and oblique variants of a font. italic requests a true italic face if one is available in the font family — a separately designed variant with letterforms specifically drawn for slanted text. oblique requests a mechanically slanted version of the regular face, used when no italic variant exists.\n1 2 3 4 5 6 7 8 9 10 11 em { font-style: italic; } .annotation { font-style: oblique; } .upright { font-style: normal; } For variable fonts that include an ital or slnt axis, font-style: oblique can accept an angle value to fine-tune the degree of slant:\n1 2 3 .custom-slant { font-style: oblique 12deg; } The range of valid angles is -90deg to 90deg. If the font does not support a slant axis, the browser synthesises the slant by skewing the regular face, which is generally considered lower quality than a true italic or oblique face.\nfont-style is inherited, and is most commonly set on emphasis elements like \u0026lt;em\u0026gt; and \u0026lt;cite\u0026gt;, or used in editorial contexts to distinguish text types such as captions, asides, or foreign-language phrases.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-style","tags":null,"title":"font-style"},{"categories":null,"content":"The font-variant shorthand controls a range of typographic glyph substitutions and variations offered by OpenType fonts. In its simplest form it toggles small caps, but it is also a shorthand for a family of longhands — font-variant-caps, font-variant-numeric, font-variant-ligatures, font-variant-alternates, font-variant-east-asian, and font-variant-emoji — each targeting different categories of typographic feature.\n1 2 3 4 5 6 7 8 9 10 11 .small-caps { font-variant: small-caps; } .numeric { font-variant-numeric: oldstyle-nums proportional-nums; } .ligatures { font-variant-ligatures: common-ligatures discretionary-ligatures; } Small caps (font-variant-caps: small-caps) replaces lowercase letters with smaller versions of the capital letterforms — either from a dedicated small caps face or synthesised by the browser. Numeric variants control how digits are rendered: lining versus oldstyle, tabular versus proportional.\nLigature control via font-variant-ligatures enables or disables the automatic substitution of letter pairs (like \u0026ldquo;fi\u0026rdquo; or \u0026ldquo;fl\u0026rdquo;) with combined glyphs. Common ligatures are typically enabled by default in browsers; discretionary and historical ligatures are off by default and can be opted into for more typographically rich display settings.\nThe effectiveness of all font-variant features depends on the font supporting the relevant OpenType features.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-variant","tags":null,"title":"font-variant"},{"categories":null,"content":"The font-weight property sets the weight (thickness) of the font. It accepts numeric values from 1 to 1000, or keyword equivalents. The most commonly used values are 400 (normal) and 700 (bold), which correspond to the normal and bold keywords.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 body { font-weight: 400; } h1 { font-weight: 700; } .light-label { font-weight: 300; } .black-headline { font-weight: 900; } Named keywords include thin (100), extra-light (200), light (300), normal (400), medium (500), semi-bold (600), bold (700), extra-bold (800), and black (900). The keywords bolder and lighter are relative, stepping up or down from the inherited weight.\nFor variable fonts with a weight axis (wght), any value within the font\u0026rsquo;s supported range can be used — not just multiples of 100. This allows fine-grained weight control for display typography or responsive weight adjustments.\nIf the requested weight is not available as a distinct face, the browser selects the nearest available weight following a defined fallback algorithm. If no heavier face is available, the browser may synthesise bold by artificially thickening the regular face, though this is generally lower quality than a true bold variant.\n","permalink":"https://www.webstf.nl/baseline/2015/#font-weight","tags":null,"title":"font-weight"},{"categories":null,"content":"CSS provides a set of pseudo-classes that reflect the validity state of form controls, allowing visual feedback to be applied based on whether a field\u0026rsquo;s current value satisfies its validation constraints — without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 input:valid { border-color: #2ecc71; } input:invalid { border-color: #e74c3c; } input:required { border-left: 3px solid var(--color-accent); } input:optional { border-left: 3px solid #ccc; } input:in-range { background: #f0fff4; } input:out-of-range { background: #fff0f0; } :valid matches when the field\u0026rsquo;s value satisfies all its constraints (required, minlength, maxlength, pattern, min, max, type, etc.). :invalid matches when it does not. :required and :optional reflect the presence or absence of the required attribute. :in-range and :out-of-range apply to \u0026lt;input type=\u0026quot;number\u0026quot;\u0026gt; and \u0026lt;input type=\u0026quot;range\u0026quot;\u0026gt; elements with min and max attributes.\nA significant usability concern with :valid and :invalid is that they apply immediately on page load, before the user has interacted with the field at all — an empty required input is :invalid from the start, which can feel jarring. The newer :user-valid and :user-invalid pseudo-classes address this by only applying after the user has interacted with and then left the field, producing a much more appropriate timing for validation feedback.\n1 2 3 4 5 6 7 input:user-invalid { border-color: #e74c3c; } input:user-valid { border-color: #2ecc71; } ","permalink":"https://www.webstf.nl/baseline/2015/#form-validity-pseudos","tags":null,"title":"Form validity pseudo-classes"},{"categories":null,"content":"getComputedStyle() is a browser JavaScript API — available as window.getComputedStyle() — that returns the final, resolved values of all CSS properties applied to an element after the cascade, inheritance, and any browser defaults have been applied. It reflects what the browser has actually computed and is rendering, not what is written in any individual stylesheet.\n1 2 3 4 5 6 7 8 9 10 const element = document.querySelector(\u0026#39;.card\u0026#39;); const styles = window.getComputedStyle(element); const fontSize = styles.getPropertyValue(\u0026#39;font-size\u0026#39;); const color = styles.getPropertyValue(\u0026#39;color\u0026#39;); const customProp = styles.getPropertyValue(\u0026#39;--brand-color\u0026#39;); console.log(fontSize); // e.g. \u0026#34;18px\u0026#34; console.log(color); // e.g. \u0026#34;rgb(26, 26, 26)\u0026#34; console.log(customProp); // e.g. \u0026#34; oklch(55% 0.2 250)\u0026#34; The returned CSSStyleDeclaration object can be queried with getPropertyValue() for any CSS property, including custom properties. Values are always returned as strings in their resolved form — lengths in pixels, colors in rgb() notation, and so on.\nAn optional second argument allows pseudo-element styles to be retrieved:\n1 2 const before = window.getComputedStyle(element, \u0026#39;::before\u0026#39;); const content = before.getPropertyValue(\u0026#39;content\u0026#39;); getComputedStyle() is read-only — it cannot be used to set styles. It is most commonly used when JavaScript needs to respond to or measure CSS-driven state, such as reading a custom property value set by a media query, measuring an element\u0026rsquo;s dimensions after layout, or checking whether a transition or animation is currently active.\n","permalink":"https://www.webstf.nl/baseline/2015/#get-computed-style","tags":null,"title":"getComputedStyle()"},{"categories":null,"content":"CSS gradients are image values generated by the browser from color stops, usable anywhere an image is accepted — most commonly in background-image. There are three main gradient types: linear, radial, and conic, each with a repeating variant.\nlinear-gradient() transitions colors along a straight line at a specified angle or direction:\n1 2 3 4 5 6 7 .banner { background-image: linear-gradient(135deg, #667eea, #764ba2); } .subtle { background-image: linear-gradient(to bottom, white, #f0f0f0); } radial-gradient() radiates colors outward from a central point in a circular or elliptical shape:\n1 2 3 .spotlight { background-image: radial-gradient(circle at 30% 40%, #fff9c4, transparent 60%); } conic-gradient() sweeps colors around a central point, like the face of a pie chart:\n1 2 3 4 .pie { background-image: conic-gradient(#f093fb 0% 25%, #f5576c 25% 60%, #4facfe 60%); border-radius: 50%; } The repeating-* variants — repeating-linear-gradient(), repeating-radial-gradient(), and repeating-conic-gradient() — tile the gradient pattern seamlessly, enabling geometric stripe and pattern effects without image files.\ncolor stops can include explicit position values and hints (midpoint control). Modern CSS gradient syntax supports explicit color space interpolation — linear-gradient(in oklch, blue, yellow) — which produces more perceptually uniform color transitions than the default sRGB interpolation.\n","permalink":"https://www.webstf.nl/baseline/2015/#gradients","tags":null,"title":"Gradients"},{"categories":null,"content":"The inherit keyword forces a property to take the computed value of the same property from its parent element. It works for both inherited and non-inherited properties — for non-inherited properties, it explicitly opts the element into the inheritance chain that would otherwise not apply.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .child { /* border does not inherit by default — this forces it to */ border: inherit; } a { /* Links do not inherit color by default in most browsers */ color: inherit; } .reset-padding { padding: inherit; } A very common use of inherit is on anchor elements. Browsers apply a default color to \u0026lt;a\u0026gt; tags that overrides the inherited text color, so setting color: inherit on links inside a component ensures they match the surrounding text color without an explicit value.\ninherit is one of the CSS-wide keywords available on every property, alongside initial (resets to the property\u0026rsquo;s default value), unset (either inherits or resets depending on whether the property is naturally inherited), and revert (rolls back to the browser\u0026rsquo;s default stylesheet value). Together these keywords give precise control over how the cascade resolves a property\u0026rsquo;s value.\n","permalink":"https://www.webstf.nl/baseline/2015/#inherit-value","tags":null,"title":"inherit"},{"categories":null,"content":"The initial keyword resets a CSS property to its initial value as defined in the CSS specification — the value the property has before any author or user stylesheet is applied. It is one of the CSS-wide keywords available on every property.\n1 2 3 4 5 6 7 8 9 10 11 .reset-color { color: initial; /* Resets to black in most cases */ } .reset-display { display: initial; /* Resets to \u0026#39;inline\u0026#39; */ } .reset-font-weight { font-weight: initial; /* Resets to \u0026#39;normal\u0026#39; (400) */ } initial is particularly useful in component-level resets where you want to return a property to a known baseline without knowing — or being able to predict — what value has been inherited or set by earlier rules.\nIt is important to understand the difference between initial and inherit. initial uses the specification-defined default, which is not necessarily what the browser\u0026rsquo;s default stylesheet sets. For example, display: initial gives inline, not block, even for elements like \u0026lt;div\u0026gt; that browsers render as block by default — because the CSS specification\u0026rsquo;s initial value for display is inline.\nThe related keyword revert is often more useful in practice: it resets the property to the value the browser\u0026rsquo;s default stylesheet would apply, rather than the specification\u0026rsquo;s abstract initial value.\n","permalink":"https://www.webstf.nl/baseline/2015/#initial-value","tags":null,"title":"initial"},{"categories":null,"content":"CSS provides a rich set of pseudo-classes specifically for styling form inputs based on their state and validity. These allow form controls to respond visually to user interaction and data validation without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 input:focus { outline: 2px solid var(--color-primary); } input:disabled { opacity: 0.5; cursor: not-allowed; } input:required { border-left: 3px solid var(--color-accent); } input:valid { border-color: green; } input:invalid { border-color: red; } input:placeholder-shown { font-style: italic; } input:checked { accent-color: var(--color-primary); } The key input-related pseudo-classes are :focus, :focus-visible, :disabled, :enabled, :checked, :required, :optional, :valid, :invalid, :in-range, :out-of-range, :read-only, :read-write, and :placeholder-shown.\n:focus-visible is particularly important — it applies focus styles only when the browser determines they are needed for accessibility (keyboard navigation), suppressing them for mouse clicks that would otherwise show a distracting ring on every clicked input.\n:user-valid and :user-invalid are newer additions that only apply validation styles after the user has interacted with the field, preventing the jarring experience of seeing an input marked as invalid before the user has had a chance to type anything.\n","permalink":"https://www.webstf.nl/baseline/2015/#input-selectors","tags":null,"title":"Input selectors"},{"categories":null,"content":"The letter-spacing property adds or removes space between characters in text. Positive values increase the spacing, negative values reduce it. The value is added on top of any kerning information in the font, rather than replacing it.\n1 2 3 4 5 6 7 8 9 10 11 12 .headline { letter-spacing: -0.02em; } .label { text-transform: uppercase; letter-spacing: 0.1em; } .tight { letter-spacing: -0.5px; } Using em units for letter-spacing is generally preferred over fixed lengths because the spacing scales proportionally with the font size. This is particularly important for responsive designs where font sizes vary.\nUppercase text is a common application for positive letter-spacing — tightly spaced capitals can feel cramped, and a small tracking increase (around 0.05em to 0.15em) significantly improves legibility for labels, captions, and headings set in all-caps. Conversely, large display type often benefits from slightly negative tracking to appear more naturally spaced.\nNote that letter-spacing also adds space after the last character in a line, which can cause visual alignment issues in centred text. This is a known limitation with no direct CSS fix, though it is rarely noticeable at typical tracking values.\n","permalink":"https://www.webstf.nl/baseline/2015/#letter-spacing","tags":null,"title":"letter-spacing"},{"categories":null,"content":"The line-height property sets the height of a line box. It controls the vertical space between lines of text and is one of the most important properties for readability. Values can be a unitless number, a length, a percentage, or the normal keyword.\n1 2 3 4 5 6 7 8 9 10 11 body { line-height: 1.6; } h1 { line-height: 1.1; } .compact { line-height: 1.3; } Using a unitless number is strongly recommended over length or percentage values. A unitless value like 1.6 means \u0026ldquo;1.6 times the element\u0026rsquo;s font-size\u0026rdquo;, and crucially, it is inherited as the multiplier itself — not as a computed pixel value. This means child elements with different font sizes will calculate their own appropriate line height from the same multiplier.\nIf a length or percentage is used instead, the computed value (in pixels) is what gets inherited, which often produces too-tight or too-loose line spacing on child elements with different font sizes.\nBody text typically reads well at 1.4–1.7. Headings, which are larger and usually shorter, tend to look better at 1.0–1.3. The lh and rlh units allow other properties to reference the computed line height directly, enabling precise vertical rhythm systems.\n","permalink":"https://www.webstf.nl/baseline/2015/#line-height","tags":null,"title":"line-height"},{"categories":null,"content":"The list-style shorthand sets the marker appearance of list items, combining list-style-type, list-style-position, and list-style-image in a single declaration. It applies to elements with display: list-item, including \u0026lt;li\u0026gt; elements.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ul { list-style: disc outside; } ol { list-style: decimal inside; } .custom-list { list-style: url(\u0026#39;/icons/checkmark.svg\u0026#39;) outside; } .no-bullets { list-style: none; } list-style-type controls the marker character — common values include disc, circle, square for unordered lists and decimal, lower-alpha, lower-roman for ordered ones. It also accepts a string value for a custom character: list-style-type: '→'.\nlist-style-position: outside (default) places the marker in the margin, outside the text flow. inside places it within the content box, causing wrapped lines to align under the marker rather than the text start.\nThe ::marker pseudo-element provides more precise styling of the marker itself — color, font-size, content, and a limited set of other properties — without affecting the list item\u0026rsquo;s content. This is the modern approach when the marker needs distinct styling rather than replacement.\n","permalink":"https://www.webstf.nl/baseline/2015/#list-style","tags":null,"title":"List style"},{"categories":null,"content":"The margin shorthand sets the outer spacing on all four sides of an element, outside its border. It accepts one to four values following the standard shorthand pattern: one value sets all sides, two values set top/bottom and left/right, three values set top, left/right, and bottom, and four values set top, right, bottom, and left individually.\n1 2 3 4 5 6 7 8 9 10 11 .section { margin: 2rem 0; /* 2rem top/bottom, 0 left/right */ } .card { margin: 1rem auto; /* 1rem top/bottom, auto left/right */ } .flush-top { margin-top: 0; } Setting margin: 0 auto on a block element with a defined width is the classic way to centre it horizontally within its container — the auto values distribute the available space equally on both sides.\nMargins on block elements in the block direction (vertically in horizontal writing modes) collapse: when two block margins are adjacent, only the larger of the two applies rather than their sum. This is called margin collapsing, and it affects top and bottom margins between sibling elements, between a parent and its first/last child, and on empty elements. Margins in flex and grid containers do not collapse.\nThe logical property equivalents — margin-block and margin-inline — set margins relative to the writing direction, adapting automatically for vertical or right-to-left text flows.\n","permalink":"https://www.webstf.nl/baseline/2015/#margin","tags":null,"title":"margin"},{"categories":null,"content":"Media queries allow CSS rules to be applied conditionally based on characteristics of the device or environment — most commonly the viewport width, but also screen resolution, color capability, user preferences, and more. They are the foundation of responsive web design.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* Mobile-first: base styles apply to all sizes */ .grid { display: grid; grid-template-columns: 1fr; gap: 1rem; } @media (width \u0026gt;= 640px) { .grid { grid-template-columns: repeat(2, 1fr); } } @media (width \u0026gt;= 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } } The modern range syntax (width \u0026gt;= 640px) is cleaner than the older min-width form and supports intuitive range expressions like (640px \u0026lt;= width \u0026lt;= 1024px).\nBeyond width, media queries can target print output, user preferences, and display capabilities:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @media print { .no-print { display: none; } } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } @media (hover: hover) { .button:hover { background: var(--color-hover); } } Container queries have taken over many use cases that previously required media queries for component-level responsive behaviour, but media queries remain the appropriate tool for viewport-level layout changes and user preference adaptation.\n","permalink":"https://www.webstf.nl/baseline/2015/#media-queries","tags":null,"title":"Media queries"},{"categories":null,"content":"CSS defines a set of named color keywords that can be used anywhere a color value is accepted. There are 148 named colors in the CSS specification, ranging from common names like red, blue, and white to more evocative ones like rebeccapurple, papayawhip, and cornflowerblue. All named colors are case-insensitive.\n1 2 3 4 5 6 7 8 9 10 11 12 .warning { background-color: gold; color: darkred; } .divider { border-color: lightgray; } .highlight { background-color: cornsilk; } Two special named color keywords deserve mention. transparent is fully transparent black (rgb(0 0 0 / 0)) and is useful for resetting backgrounds, creating fade effects in gradients, or hiding borders while preserving their spacing. currentcolor is a dynamic value that resolves to the element\u0026rsquo;s computed color value, allowing other properties to inherit the text color without explicitly repeating it.\nNamed colors are defined in the sRGB color space and have fixed values. For production design work they are most useful for quick prototyping and utility values like transparent, currentcolor, and white/black — precise color palettes are better expressed with hex, rgb(), hsl(), or oklch() for full control and wider gamut support.\n","permalink":"https://www.webstf.nl/baseline/2015/#named-color","tags":null,"title":"Named colors"},{"categories":null,"content":"The opacity property sets the transparency of an element and all of its contents as a group. It accepts a value between 0 (fully transparent) and 1 (fully opaque), or a percentage equivalent.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .disabled { opacity: 0.4; } .overlay { opacity: 0.8; } .fade-in { opacity: 0; transition: opacity 0.3s ease; } .fade-in.visible { opacity: 1; } A key characteristic of opacity is that it applies to the entire element as a composited unit — it does not apply individually to each child. This means a child element cannot be made more opaque than its parent. If you want a semi-transparent background without affecting the text or children, use a color with an alpha channel on background-color instead: background-color: rgb(0 0 0 / 0.5).\nSetting opacity to any value less than 1 creates a new stacking context, which affects how the element and its descendants interact with z-index. This is one of several properties — alongside transform, filter, and will-change — that implicitly establish a stacking context as a side effect.\nopacity is animatable and GPU-accelerated in most browsers, making it one of the most performant properties to transition.\n","permalink":"https://www.webstf.nl/baseline/2015/#opacity","tags":null,"title":"opacity"},{"categories":null,"content":"The padding shorthand sets the inner spacing between an element\u0026rsquo;s content and its border on all four sides. Like margin, it accepts one to four values following the shorthand pattern for top, right, bottom, and left.\n1 2 3 4 5 6 7 8 9 10 11 .button { padding: 0.75rem 1.5rem; } .card { padding: 1.5rem; } .icon-button { padding: 0.5rem; } Unlike margins, padding cannot be negative and does not collapse. Padding is part of the element\u0026rsquo;s clickable and background-painted area — a larger padding increases the interactive target size of a button, for example, and the background color fills the padding region.\nThe relationship between padding and box-sizing is important: with the default box-sizing: content-box, padding is added outside the specified width and height, making the rendered element larger than its defined dimensions. With box-sizing: border-box, padding is included within the specified dimensions, which is typically easier to reason about and is a common global reset.\nLogical equivalents — padding-block and padding-inline — set padding relative to the writing direction and are the preferred approach for internationalised layouts or components used in both horizontal and vertical writing modes.\n","permalink":"https://www.webstf.nl/baseline/2015/#padding","tags":null,"title":"padding"},{"categories":null,"content":"Physical properties in CSS are those that refer to absolute directions in the viewport: top, right, bottom, left, width, and height. These are contrasted with logical properties, which use writing-mode-relative axes — block and inline — that adapt to different text directions and orientations.\n1 2 3 4 5 6 7 /* Physical properties — always refer to the same sides */ .box { margin-top: 1rem; padding-left: 1.5rem; border-right: 1px solid #ccc; width: 300px; } Physical properties are straightforward for layouts that will only ever be consumed in a single writing direction (left-to-right, top-to-bottom). However, they require manual overriding when supporting right-to-left languages like Arabic or Hebrew, or vertical writing modes used in some East Asian typography.\nLogical properties (margin-block-start, padding-inline-start, border-inline-end, inline-size, block-size) do the same job but map their values to the appropriate physical side based on the current writing-mode and direction. For internationalised projects or design systems intended to support multiple writing modes, logical properties are the more robust choice. For simple, single-direction layouts, physical properties remain clear and direct.\n","permalink":"https://www.webstf.nl/baseline/2015/#physical-properties","tags":null,"title":"Physical properties"},{"categories":null,"content":"The pointer-events property controls whether an element can be the target of mouse, touch, and stylus events. Setting it to none makes the element completely transparent to pointer interaction — clicks, hovers, and taps pass through to whatever is underneath.\n1 2 3 4 5 6 7 8 9 10 11 12 .overlay { pointer-events: none; /* Clicks pass through to elements below */ } .disabled-button { pointer-events: none; opacity: 0.5; } .interactive { pointer-events: auto; /* Default — normal event behaviour */ } pointer-events: none is commonly used for decorative overlay elements that should not interfere with interaction, for visually disabled controls, and during animations where click events should be suppressed. It is also used in layered UI systems where an absolutely positioned transparent layer needs to be visually present but not capture events.\nA useful pattern is applying pointer-events: none to a parent while restoring it on specific children:\n1 2 3 4 5 6 7 .modal-backdrop { pointer-events: none; } .modal-backdrop .modal { pointer-events: auto; /* The modal itself is still interactive */ } For SVG elements, pointer-events accepts additional values beyond none and auto — such as visiblePainted, visibleFill, visibleStroke, visible, painted, fill, stroke, and all — providing fine-grained control over which parts of an SVG shape respond to events.\nNote that pointer-events: none does not affect keyboard focus or accessibility — elements remain in the tab order and can still be activated via keyboard unless explicitly addressed.\n","permalink":"https://www.webstf.nl/baseline/2015/#pointer-events","tags":null,"title":"pointer-events"},{"categories":null,"content":"The position property establishes how an element is placed in a document and determines whether it creates a new positioning context for descendants. Five values are available: static, relative, absolute, fixed, and sticky.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .parent { position: relative; /* Establishes positioning context */ } .overlay { position: absolute; inset: 0; /* Shorthand for top/right/bottom/left: 0 */ } .nav { position: sticky; top: 0; } static is the default — the element follows normal document flow with no special positioning. relative keeps the element in flow but allows it to be offset from its natural position using top, right, bottom, and left, without affecting surrounding elements. It also establishes a containing block for absolutely positioned descendants.\nabsolute removes the element from flow and positions it relative to the nearest non-static ancestor. fixed positions relative to the viewport. sticky is a hybrid: the element remains in flow and behaves like relative until it reaches a specified scroll threshold, at which point it sticks like a fixed element within its scroll container.\nThe inset shorthand property sets top, right, bottom, and left in one declaration, following the same one-to-four value pattern as margin and padding.\n","permalink":"https://www.webstf.nl/baseline/2015/#position","tags":null,"title":"Position"},{"categories":null,"content":"Relative positioning moves an element from its natural position in the document flow, offset by specified values, while the space it originally occupied is preserved. Surrounding elements are not affected — the layout behaves as if the element is still in its original position.\n1 2 3 4 5 6 7 8 9 10 .nudged { position: relative; top: -2px; /* Move up by 2px from natural position */ left: 4px; } .badge { position: relative; top: -0.25em; /* Slight upward shift for visual alignment */ } Because position: relative does not remove the element from flow, it is rarely used for significant layout repositioning — large offsets produce visual overlap without rearranging the surrounding content. Its most common uses are small optical adjustments and, more importantly, establishing a containing block for absolutely positioned children.\n1 2 3 4 5 6 7 8 9 .card { position: relative; /* Makes .card the containing block for .badge */ } .badge { position: absolute; top: 0.5rem; right: 0.5rem; } Setting position: relative on a container — even without any offset values — is the standard pattern for scoping absolutely positioned descendants to that container.\n","permalink":"https://www.webstf.nl/baseline/2015/#relative-positioning","tags":null,"title":"Relative positioning"},{"categories":null,"content":"The rem unit (root em) is a relative length unit equal to the computed font-size of the root element — the \u0026lt;html\u0026gt; element. Unlike em, which is relative to the current element\u0026rsquo;s font size and can compound when nested, rem always references the same root value, making it consistent and predictable throughout a stylesheet.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 :root { font-size: 16px; /* 1rem = 16px everywhere */ } h1 { font-size: 2.5rem; /* 40px */ } p { font-size: 1rem; /* 16px */ margin-bottom: 1rem; /* 16px */ } .small { font-size: 0.875rem; /* 14px */ } Because rem is anchored to the root, it does not suffer from the compounding problem of em — a deeply nested element using rem gets the same value as a top-level element. This makes rem the preferred unit for font sizes and spacing in design systems, where consistent scale is important.\nA common practice is to leave the root font-size unset (or set it to a percentage rather than an absolute value), allowing it to respect the user\u0026rsquo;s browser font size preference. Setting font-size: 62.5% on :root was historically used to make 1rem equal 10px for easier mental arithmetic, though this approach can interfere with user accessibility settings and is less favoured today.\n","permalink":"https://www.webstf.nl/baseline/2015/#rem","tags":null,"title":"rem"},{"categories":null,"content":"CSS selectors identify which elements a rule\u0026rsquo;s declarations apply to. The core selector types form the building blocks for targeting any element or group of elements in a document.\nType selectors match elements by their tag name. Class selectors (.class) match elements with a given class attribute. ID selectors (#id) match a single element with a given ID. The universal selector (*) matches any element.\n1 2 3 4 p { } /* Type selector */ .card { } /* Class selector */ #header { } /* ID selector */ * { } /* Universal selector */ Combinators express relationships between elements: the descendant combinator (space) matches any descendant, the child combinator (\u0026gt;) matches direct children only, the adjacent sibling combinator (+) matches the immediately following sibling, and the general sibling combinator (~) matches all following siblings.\n1 2 3 4 article p { } /* Any p inside article */ article \u0026gt; p { } /* Direct child p of article */ h2 + p { } /* p immediately after h2 */ h2 ~ p { } /* All p siblings after h2 */ Attribute selectors match elements based on their attributes and values:\n1 2 3 4 5 6 [href] { } /* Has href attribute */ [type=\u0026#34;submit\u0026#34;] { } /* Exact value match */ [class~=\u0026#34;icon\u0026#34;] { } /* Space-separated word match */ [href^=\u0026#34;https\u0026#34;] { } /* Starts with */ [href$=\u0026#34;.pdf\u0026#34;] { } /* Ends with */ [href*=\u0026#34;example\u0026#34;] { } /* Contains */ Selector lists (,) apply a rule to multiple selectors at once. Specificity — determined by the types of selectors used — controls which rule wins when multiple rules target the same element.\n","permalink":"https://www.webstf.nl/baseline/2015/#selectors","tags":null,"title":"Selectors (core)"},{"categories":null,"content":"Static positioning is the default positioning behaviour for all elements. An element with position: static is placed according to the normal document flow — block elements stack vertically, inline elements flow horizontally — and the offset properties top, right, bottom, and left have no effect.\n1 2 3 .element { position: static; /* Default — usually not written explicitly */ } Because static is the default, it is rarely declared explicitly. The main reason to set it is to reset an element that has inherited or been given a non-static position, returning it to normal flow behaviour.\nStatically positioned elements do not create a positioning context, so absolutely positioned descendants will look past them to the nearest non-static ancestor (or the initial containing block) to determine their position.\nUnderstanding static positioning matters most when reasoning about which ancestor an absolutely positioned element is relative to — scanning up the DOM for the first element with a position value other than static identifies the containing block.\n","permalink":"https://www.webstf.nl/baseline/2015/#static-positioning","tags":null,"title":"Static positioning"},{"categories":null,"content":"The style HTML attribute allows CSS declarations to be applied directly to an individual element in the markup. Styles set this way are called inline styles and have the highest specificity of any author style — they override rules from stylesheets unless those rules use !important.\n1 2 \u0026lt;p style=\u0026#34;color: red; font-weight: bold;\u0026#34;\u0026gt;Important notice\u0026lt;/p\u0026gt; \u0026lt;div style=\u0026#34;--accent: oklch(60% 0.2 30);\u0026#34;\u0026gt;...\u0026lt;/div\u0026gt; Inline styles are useful in specific situations: server-generated dynamic values that cannot be known at stylesheet authoring time (such as progress bar widths, chart colors, or user-defined theme colors), and CSS custom property values that feed into a component\u0026rsquo;s stylesheet.\n1 \u0026lt;div class=\u0026#34;progress-bar\u0026#34; style=\u0026#34;--progress: 72%\u0026#34;\u0026gt; 1 2 3 .progress-bar::after { width: var(--progress); } For general styling, inline styles are considered poor practice — they are harder to maintain, cannot be overridden without !important, cannot use pseudo-classes or pseudo-elements, and mix concerns between structure and presentation. Stylesheet-based styling is preferred for everything except genuinely dynamic, per-element values.\nInline styles can also be read and set via JavaScript with element.style, which provides programmatic access to individual CSS properties on an element.\n","permalink":"https://www.webstf.nl/baseline/2015/#style-attr","tags":null,"title":"style (attribute)"},{"categories":null,"content":"System colors are a set of CSS color keywords that resolve to the colors used by the operating system\u0026rsquo;s UI. They allow web content to automatically adopt the user\u0026rsquo;s system color scheme — including high-contrast modes and custom themes — without explicitly querying prefers-color-scheme or hardcoding dark/light palette variants.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .button { background-color: ButtonFace; color: ButtonText; border: 1px solid ButtonBorder; } .input { background-color: Field; color: FieldText; } body { background-color: Canvas; color: CanvasText; } The CSS Color specification defines a standard set of system color keywords including Canvas, CanvasText, LinkText, ButtonFace, ButtonText, ButtonBorder, Field, FieldText, Highlight, HighlightText, AccentColor, and AccentColorText, among others.\nSystem colors are particularly valuable for accessibility. Users who rely on high-contrast modes or custom system themes do so because the default colors are unusable for them. Components that use system colors respect those preferences automatically, whereas components with hardcoded colors may remain inaccessible even when forced colors mode is active. Using system colors is the recommended approach for ensuring custom UI components remain accessible under forced-color modes.\n","permalink":"https://www.webstf.nl/baseline/2015/#system-color","tags":null,"title":"System colors"},{"categories":null,"content":"The text-overflow property controls how text that overflows its container is visually indicated when overflow is hidden. The most common value, ellipsis, replaces the clipped text with a \u0026ldquo;…\u0026rdquo; character to signal that content has been truncated. It only takes effect when the element has overflow set to a value other than visible and white-space: nowrap to prevent the text from wrapping.\n1 2 3 4 5 .truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } This three-property combination is the standard pattern for single-line text truncation — a card title, a table cell, or a file name that must fit within a fixed width.\nFor multi-line truncation (clamping text to a set number of lines), -webkit-line-clamp is the widely supported approach:\n1 2 3 4 5 6 .clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; } text-overflow accepts two values to set the overflow indicator for the start and end of the text independently, which is useful for bidirectional text. The clip value (default) simply cuts the text off at the container edge without any indicator character.\ntext-overflow only affects the visual rendering — the full text content remains in the DOM and is accessible to screen readers and search engines.\n","permalink":"https://www.webstf.nl/baseline/2015/#text-overflow","tags":null,"title":"Text overflow"},{"categories":null,"content":"The text-align property sets the horizontal alignment of inline content (text, inline elements) within a block container. The main values are left, right, center, justify, and the logical keywords start and end.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 h1 { text-align: center; } p { text-align: start; /* Left in LTR, right in RTL */ } .price { text-align: right; } .body-text { text-align: justify; } start and end are the logical equivalents of left and right — they adapt to the text\u0026rsquo;s writing direction (ltr or rtl) rather than referring to fixed physical sides. Using start instead of left is better practice for internationalised content.\ntext-align: justify stretches lines to fill the full container width by adjusting spacing between words. It is common in print design but can produce uneven word spacing in narrow containers on the web, particularly without hyphenation enabled. Pairing text-align: justify with hyphens: auto reduces the severity of large spacing gaps.\ntext-align is inherited, so it can be set on a container to apply to all inline content within it.\n","permalink":"https://www.webstf.nl/baseline/2015/#text-align","tags":null,"title":"text-align"},{"categories":null,"content":"The text-indent property indents the first line of a block of text by a specified amount. It is the CSS equivalent of a paragraph indent in word processing, and is the conventional way to mark paragraph breaks in certain typographic styles — particularly those that omit the space between paragraphs in favour of an indent.\n1 2 3 p + p { text-indent: 1.5em; } Negative values produce a hanging indent, where the first line extends to the left of the text block and subsequent lines are indented:\n1 2 3 4 .hanging { padding-left: 2rem; text-indent: -2rem; } Hanging indents are useful for definition lists, bibliographies, and any list-like content where a label or initial text should stand apart from the wrapped body.\nTwo optional keywords modify the behaviour: each-line applies the indent after every hard line break (not just the first line), and hanging inverts the indent so it applies to all lines except the first, which is useful shorthand for the negative + padding pattern above.\ntext-indent is inherited and accepts length and percentage values. Percentage values are relative to the containing block\u0026rsquo;s width.\n","permalink":"https://www.webstf.nl/baseline/2015/#text-indent","tags":null,"title":"text-indent"},{"categories":null,"content":"The text-shadow property adds one or more shadows to text. Each shadow is defined by a horizontal offset, a vertical offset, an optional blur radius, and a color. Multiple shadows can be stacked in a comma-separated list, with the first shadow appearing on top.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .heading { text-shadow: 2px 2px 4px rgb(0 0 0 / 0.3); } .glow { text-shadow: 0 0 8px oklch(70% 0.3 200); } .outlined { text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; } The offset values move the shadow from the text\u0026rsquo;s position: positive horizontal offsets go right, positive vertical offsets go down. The blur radius is optional (defaults to 0 for a hard-edged shadow) and controls how spread out and soft the shadow appears.\nMultiple shadows can be used together to create outline effects, layered glows, or complex decorative treatments. The stacking example above uses four 1px shadows at diagonal positions to simulate a text outline — a technique predating paint-order and useful in contexts where it remains necessary.\nUnlike box-shadow, text-shadow does not support an inset keyword or a spread radius.\n","permalink":"https://www.webstf.nl/baseline/2015/#text-shadow","tags":null,"title":"text-shadow"},{"categories":null,"content":"The text-transform property controls the capitalisation of text, converting it to uppercase, lowercase, or title case visually without changing the underlying HTML content. The transformation is purely presentational — the source text remains unchanged in the DOM.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .nav-link { text-transform: uppercase; } .byline { text-transform: lowercase; } .page-title { text-transform: capitalize; } .normal { text-transform: none; } uppercase converts all characters to their uppercase equivalents. lowercase converts all to lowercase. capitalize converts the first character of each word to uppercase, leaving the rest unchanged. none is the default, applying no transformation.\nBecause text-transform operates on the rendered output without modifying the DOM, it is accessible — screen readers read the original source text, avoiding the awkward letter-by-letter reading that can occur when text is manually written in all caps. This makes text-transform: uppercase the recommended approach for all-caps labels and headings rather than writing them in uppercase in the HTML.\nThe full-width and full-size-kana values are additional options for specific East Asian typography needs.\n","permalink":"https://www.webstf.nl/baseline/2015/#text-transform","tags":null,"title":"text-transform"},{"categories":null,"content":"CSS transitions animate the change of a property\u0026rsquo;s value from one state to another over a specified duration. When a property value changes — due to a class toggle, pseudo-class like :hover, or JavaScript — the transition interpolates smoothly between the old and new values.\n1 2 3 4 5 6 7 8 9 10 .button { background-color: #3a6bc4; transform: translateY(0); transition: background-color 0.2s ease, transform 0.2s ease; } .button:hover { background-color: #2a5bb4; transform: translateY(-2px); } The transition shorthand sets transition-property, transition-duration, transition-timing-function, and transition-delay for one or more properties. Multiple transitions are comma-separated.\n1 2 3 4 5 6 .panel { transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background-color 0.15s ease; } Using transition-property: all applies the transition to every animatable property, which is convenient but can produce unexpected results and is less performant than specifying properties explicitly. transform and opacity are the most performant properties to transition as they are GPU-composited and do not trigger layout or paint.\ntransition-delay sets a wait time before the transition begins — useful for sequencing effects or delaying a hover state from activating immediately.\nCombined with @starting-style and transition-behavior: allow-discrete, transitions can now also animate discrete properties like display and content-visibility, enabling enter and exit animations on elements that toggle in and out of the DOM.\n","permalink":"https://www.webstf.nl/baseline/2015/#transitions","tags":null,"title":"Transitions (CSS)"},{"categories":null,"content":"User action pseudo-classes match elements based on how the user is currently interacting with them. They are the primary tool for providing interactive feedback — hover states, focus indicators, and active press effects — without JavaScript.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .button:hover { background: var(--color-primary-hover); } .button:active { transform: scale(0.98); } .link:focus { outline: 2px solid var(--color-focus); outline-offset: 2px; } .input:focus-visible { outline: 2px solid var(--color-focus); } The four core user action pseudo-classes are :hover, :active, :focus, and :focus-visible.\n:hover applies while the pointer is over the element. It should be used thoughtfully — touch devices have no hover state, so critical functionality should never depend on it.\n:active applies while an element is being activated — typically during a mouse click or touch press. It is brief but provides important tactile feedback.\n:focus applies when an element has received focus, either via keyboard, mouse, or touch. All interactive elements should have a visible :focus style for accessibility.\n:focus-visible is the more refined version of :focus — it applies only when the browser determines a focus indicator is needed for accessibility, suppressing it for mouse/touch interactions while still showing it for keyboard navigation. It is now the recommended approach for focus styling, replacing the older practice of outline: none on :focus with mouse-click exceptions.\n","permalink":"https://www.webstf.nl/baseline/2015/#user-action-pseudos","tags":null,"title":"User action pseudo-classes"},{"categories":null,"content":"The vertical-align property controls the vertical alignment of an inline or table-cell element relative to the line box or table row it sits in. It does not apply to block-level elements — for those, flexbox or grid alignment properties are appropriate.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 img { vertical-align: middle; /* Align image midpoint to text midpoint */ } sup { vertical-align: super; } sub { vertical-align: sub; } .icon { vertical-align: -0.125em; /* Slight downward nudge */ } td { vertical-align: top; } Keyword values include baseline (default), top, middle, bottom, text-top, text-bottom, sub, and super. Length and percentage values offset the element relative to its baseline — positive values move it up, negative values move it down.\nvertical-align: middle aligns the element\u0026rsquo;s midpoint with the middle of the lowercase letters of the surrounding text (the x-height midpoint), not the exact centre of the line box. Small downward offsets with a negative length value are often needed for precise optical alignment of icons with adjacent text.\nIn table cells, vertical-align: top, middle, and bottom align the cell\u0026rsquo;s content within the row height — a separate use case unrelated to inline alignment.\n","permalink":"https://www.webstf.nl/baseline/2015/#vertical-align","tags":null,"title":"vertical-align"},{"categories":null,"content":"The visibility property shows or hides an element while preserving the space it occupies in the layout. This distinguishes it from display: none, which removes the element from the layout entirely and collapses its space.\n1 2 3 4 5 6 7 8 9 10 11 .hidden { visibility: hidden; /* Invisible but still occupies space */ } .visible { visibility: visible; } .collapsed { visibility: collapse; /* For table rows/columns: collapses space */ } visibility: hidden makes the element invisible and removes it from the accessibility tree — it cannot be focused or interacted with — but its box remains in the document flow, keeping surrounding elements in place. This is useful when toggling the presence of an element that other layout depends on, and you do not want a layout shift when it appears and disappears.\nAn important inheritance behaviour: visibility is inherited, but a child can override it. Setting visibility: hidden on a parent and visibility: visible on a child makes the child visible while the parent remains invisible — a technique for hiding a container while selectively revealing individual children.\nvisibility: collapse is specifically designed for table elements — it removes a row or column from the table layout without affecting the table\u0026rsquo;s column widths, similar to how display: none would work but without the column width changes. In non-table contexts it behaves like hidden.\n","permalink":"https://www.webstf.nl/baseline/2015/#visibility","tags":null,"title":"visibility"},{"categories":null,"content":"The white-space property controls how white space characters (spaces, tabs, newlines) in the source HTML are handled, and whether text wraps to the next line when it reaches the edge of its container. It is a shorthand for the longhands white-space-collapse and text-wrap-mode.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .code-block { white-space: pre; /* Preserve all white space, no wrapping */ } .no-wrap { white-space: nowrap; /* Collapse white space, prevent wrapping */ } .pre-wrap { white-space: pre-wrap; /* Preserve white space, allow wrapping */ } p { white-space: normal; /* Default: collapse white space, wrap */ } normal is the default: multiple spaces and newlines in the source are collapsed to a single space, and text wraps as needed. nowrap does the same collapsing but prevents wrapping — text overflows its container rather than breaking to a new line. pre preserves all white space and newlines exactly as written, without wrapping — the behaviour of the \u0026lt;pre\u0026gt; element. pre-wrap preserves white space but still allows wrapping. pre-line collapses spaces but preserves newlines.\nwhite-space: nowrap is commonly used on labels, navigation items, and table cells where wrapping would break a layout. pre and pre-wrap are essential for displaying code, poetry, or any content where the author\u0026rsquo;s line breaks are meaningful.\n","permalink":"https://www.webstf.nl/baseline/2015/#white-space","tags":null,"title":"white-space"},{"categories":null,"content":"The width and height properties set the dimensions of an element\u0026rsquo;s content area (or border area, when box-sizing: border-box is set). They accept length values, percentages, the auto keyword, and intrinsic size keywords.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .fixed { width: 320px; height: 200px; } .fluid { width: 100%; max-width: 60ch; } .square { width: 4rem; height: 4rem; } .intrinsic { width: fit-content; } auto is the default for both properties and lets the browser calculate the size: width: auto on a block element fills the available container width, while height: auto sizes to the content. Percentage values are relative to the containing block\u0026rsquo;s corresponding dimension.\nThe intrinsic size keywords — min-content, max-content, and fit-content — size the element based on its content rather than the container. min-content shrinks to the smallest size without overflow, max-content expands to fit all content without wrapping, and fit-content behaves like max-content up to the available space then shrinks like min-content.\nmin-width, max-width, min-height, and max-height constrain the element\u0026rsquo;s dimensions within a range, and take precedence over width and height when the computed values conflict.\n","permalink":"https://www.webstf.nl/baseline/2015/#width-height","tags":null,"title":"Width and height"},{"categories":null,"content":"The word-break property controls where line breaks occur within words. It is distinct from overflow-wrap, which only breaks words as a last resort to prevent overflow — word-break can aggressively break all words at any character boundary, regardless of whether they would overflow.\n1 2 3 4 5 6 7 8 9 10 11 .normal { word-break: normal; /* Default: break at standard word boundaries */ } .break-all { word-break: break-all; /* Break any word at any character */ } .keep-all { word-break: keep-all; /* Prevent breaks in CJK text */ } normal uses the default line breaking rules for the language. For Latin text this means breaking only at spaces and hyphens; for CJK (Chinese, Japanese, Korean) text it allows breaks between any two characters.\nbreak-all breaks words at any character boundary to prevent overflow, regardless of the language. This is useful for long technical strings, code, or user-generated content where words might be arbitrary sequences of characters. It affects all words, not just those that overflow — which can produce awkward-looking breaks in regular text.\nkeep-all prevents CJK text from breaking between characters, requiring spaces or punctuation as break points — similar to the behaviour of Latin text. It has no effect on non-CJK text.\nFor breaking only long overflow-causing words without affecting normal word wrapping, overflow-wrap: break-word is usually more appropriate than word-break: break-all.\n","permalink":"https://www.webstf.nl/baseline/2015/#word-break","tags":null,"title":"word-break"},{"categories":null,"content":"The word-break property controls how words break when they overflow their container. The break-word value — now better expressed as overflow-wrap: break-word — forces long unbreakable strings such as URLs, long technical identifiers, or strings without natural break points to wrap rather than overflow.\n1 2 3 4 5 6 7 8 .user-content { word-break: break-word; /* Legacy value, widely supported */ } /* Modern equivalent, preferred */ .user-content { overflow-wrap: break-word; } word-break: break-word was a non-standard value originally introduced by Internet Explorer and subsequently adopted across browsers for compatibility. It behaves identically to overflow-wrap: break-word — breaking words only when they would otherwise overflow, rather than breaking all words aggressively. The overflow-wrap property is the standardised version of what was historically also called word-wrap.\nThe standard word-break values are normal (default), break-all (breaks at any character, not just at overflow, which affects all words not just long ones), and keep-all (prevents breaks in CJK text). break-word was never in the specification as a value for word-break, though browsers honour it as an alias.\nFor new code, overflow-wrap: break-word is preferred. For maximum compatibility with older browsers in legacy codebases, both are sometimes declared together:\n1 2 3 4 .safe { word-break: break-word; /* Legacy */ overflow-wrap: break-word; /* Standard */ } ","permalink":"https://www.webstf.nl/baseline/2015/#word-break-break-word","tags":null,"title":"word-break: break-word"},{"categories":null,"content":"The word-spacing property adds or removes space between words. The value is added on top of the normal word spacing defined by the font, so word-spacing: 0 is not the same as the default normal — use normal to restore the font\u0026rsquo;s intended spacing.\n1 2 3 4 5 6 7 8 9 10 11 .spread-heading { word-spacing: 0.25em; } .tight-caption { word-spacing: -0.05em; } .reset { word-spacing: normal; } Positive values increase the space between words; negative values reduce it. Like letter-spacing, using em units is preferred because the adjustment scales proportionally with the font size.\nword-spacing is most useful for display text and headings where fine-tuning the rhythm between words contributes to a specific typographic effect. For body text, significant changes to word spacing can impair readability — it is better left at normal or adjusted only very subtly. The property is also applied to spaces between inline elements, not just between text words, which is occasionally useful for spacing inline-block or inline-flex items.\nword-spacing is an inherited property.\n","permalink":"https://www.webstf.nl/baseline/2015/#word-spacing","tags":null,"title":"word-spacing"},{"categories":null,"content":"SVG 1.1 defined its own set of writing-mode values — lr, lr-tb, rl, rl-tb, tb, and tb-rl — which predate the CSS Writing Modes specification and used different naming conventions. These values were adopted into CSS for SVG compatibility but are now considered legacy and deprecated in favour of the standard CSS values.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* SVG 1.1 legacy values (deprecated) */ text { writing-mode: tb-rl; /* Top to bottom, right to left — now: vertical-rl */ } text { writing-mode: lr-tb; /* Left to right, top to bottom — now: horizontal-tb */ } /* Modern CSS equivalents */ text { writing-mode: vertical-rl; } text { writing-mode: horizontal-tb; } The mapping between legacy and modern values is straightforward: lr and lr-tb map to horizontal-tb; rl and rl-tb also map to horizontal-tb (with direction: rtl for right-to-left); tb and tb-rl map to vertical-rl.\nModern browsers continue to support the legacy SVG values for backwards compatibility, but they should not be used in new code. The CSS Writing Modes values (horizontal-tb, vertical-rl, vertical-lr, sideways-rl, sideways-lr) are the standard and work consistently in both HTML and SVG contexts.\nWhen authoring inline SVG within HTML documents, the CSS writing mode properties apply and the modern values should be used.\n","permalink":"https://www.webstf.nl/baseline/2015/#writing-mode-svg-values","tags":null,"title":"writing-mode SVG 1.1 values"},{"categories":null,"content":"The z-index property controls the stacking order of a positioned element along the z-axis — which elements appear in front of or behind others when they overlap. Higher values stack in front of lower values. It only takes effect on elements with a position value other than static, or on flex and grid items.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 .modal-overlay { position: fixed; z-index: 100; } .modal { position: fixed; z-index: 101; } .tooltip { position: absolute; z-index: 10; } z-index values are integers and can be negative. The default value auto means the element participates in the stacking context of its parent without establishing a new one.\nThe most important concept for understanding z-index behaviour is the stacking context. Certain CSS properties create a new stacking context — position with a non-auto z-index, opacity less than 1, transform, filter, isolation: isolate, and several others. Within a stacking context, z-index values only compete with siblings in the same context; a child element cannot be stacked below its parent\u0026rsquo;s stacking context, regardless of how low its z-index is set.\nThis is the most common source of z-index confusion: an element with z-index: 9999 can still appear behind another element if it is contained within a lower-stacking-context parent. Using isolation: isolate on a component creates a contained stacking context without side effects, preventing internal z-index values from interfering with the rest of the page.\n","permalink":"https://www.webstf.nl/baseline/2015/#z-index","tags":null,"title":"z-index"}]