Mixins
Mixins are the primary API of css-is-awesome. Every visual decision is token-driven — mixin bodies read from CSS custom properties that the active theme defines, so the same @include produces a sketchbook button, a brutalist button or a corporate button depending on the loaded theme. Consume mixins from your own SCSS and the system stays small, fast and easy to re-skin.
Import setup
Two @use lines cover the whole surface. _mixins.scssis the barrel for core layout, typography, colour, interactive and effect mixins — the atomic vocabulary. Each component partial in scss/components/* exports its own composite mixins (btn, card-base, input-base, etc.) so you only import the components you need and your compiled CSS stays lean.
// your-app.scss @use 'cia/scss/mixins' as m; @use 'cia/scss/components/buttons' as b; @use 'cia/scss/components/forms' as f; .my-cta { @include b.btn(primary); @include m.elevation(2); }
The rest of this page is a reference of every public mixin, grouped by category. Internal helpers (anything prefixed with _ or used only by _generator.scss) are omitted.
Bare-tag styling (opt-in)
By default the library does not touch bare HTML — drop the CSS and you get tokens, utilities and React components, but raw <button>, <table> and <h1> stay browser-default. That keeps the library non-invasive: dropping it into an existing project never silently restyles your nav links or third-party components.
If you want a Pico-style "drop-in and it looks decent" experience, add the bare-tags recipe in one line at the top of your app's SCSS entry:
// your-app.scss @use 'css-is-awesome/scss/recipes/bare-tags'; // done — h1-h6, p, code, pre, hr, ul, ol, table, // button, input, select, textarea, label, details // all styled with the active theme.
The recipe uses normal selectors (specificity 0,0,1) — no @layer or :where() machinery. Override anything with any class-based selector and it wins automatically:
.checkout button { background: gold; // (0,1,1) > (0,0,1) — wins }
Heads up: bare-tag styling is global. If you mount third-party React components that render their own <button> internally (Radix, react-select, react-datepicker, cmdk), they will inherit these rules. Either skip the recipe and write your own scoped wrappers, or copy the recipe contents into a .cia-prose wrapper in your own SCSS to scope it.
Prefer to roll your own? The recipe is just one-line includes per tag — copy the file or write your own:
@use 'css-is-awesome/scss/mixins' as m; @use 'css-is-awesome/scss/components/buttons' as b; @use 'css-is-awesome/scss/components/data' as d; button { @include b.btn(primary); } table { @include d.table-base; } h1 { @include m.type(heading-1); }
Layout
Layout mixins cover flex helpers, responsive grids, page-level scaffolding, containers and dividers. All spacing arguments accept a numeric space token (1–9) or a t-shirt alias (xs/sm/md/lg/xl).
flex
The one flex primitive. Pass only what differs from the defaults (row / center cross-axis / start main-axis / nowrap / no gap). $justify and $align accept the shorthand start / end / center / between / around / evenly (mapping to flex-start, space-between, etc.). Full CSS values still pass through.
// signature @mixin flex($direction: row, $gap: null, $align: center, $justify: start, $wrap: nowrap, $inline: false); // perfectly centered children .hero { @include m.flex($justify: center); } // header bar / accordion trigger .toolbar { @include m.flex($justify: between, $gap: 3); } // vertical stack with gap .feed { @include m.flex($direction: column, $gap: 4); } // inline-flex chip lockup .chip { @include m.flex($inline: true, $gap: 2); }
flow
One-word switchable flex default — a preset layer over the existing flex engine (no duplicated property logic). Pass a preset keyword to switch the whole config; optionally pass $at + $then to swap the axis at a breakpoint. Presets: row (default), col, wrap, center, between, around. The semantic primitives stack / cluster / toolbar stay — they read better as named patterns; flow is the generic switchable workhorse.
@mixin flow($preset: row, $gap: 4, $at: null, $then: null); // sensible flex row .bar { @include m.flow; } // switch row → column .menu { @include m.flow(col); } // pushed apart .toolbar { @include m.flow(between); } // responsive axis switch — row desktop, column below md .cardrow { @include m.flow(row, $at: md, $then: col); }
flow-switchable
Runtime flex switching with zero recompile. Drop on any element; toggle data-flow="..." at runtime. Emits one selector-based rule per preset in the registry. Pairs with the flow presets above.
@mixin flow-switchable($gap: 4); .bar { @include m.flow-switchable; } // HTML: <div class='bar' data-flow='between'>…</div> // JS: el.dataset.flow = 'col';
wrap
Page-width wrap with responsive horizontal padding. Sizes: sm, md, lg, xl, 2xl, full. (Renamed from container in v0.8 — the namer mixin for container queries kept the container name; this layout-wrap mixin became wrap to avoid the collision.)
@mixin wrap($size: xl, $px: null); .page { @include m.wrap(lg); }
grid
Responsive CSS grid. Pass a column count, auto for auto-fit, or a minimum track size. Collapses to one column below the supplied breakpoint.
@mixin grid($cols: 1, $gap: 4, $bp: md, $min: null); .gallery { @include m.grid(3, $gap: 5); } .cards { @include m.grid(auto, $min: 250px); }
subgrid
Child inherits the parent grid's column or row tracks. Direction: columns, rows, both.
@mixin subgrid($direction: columns); .card { @include m.subgrid; grid-column: span 3; }
page-layout
Full-page grid with sticky footer + automatic mobile collapse below the breakpoint. Variants: default, sidebar-left, sidebar-right, holy-grail. Pair with page-header, page-main, page-footer, page-sidebar, page-nav, page-aside grid-area helpers. For runtime switching, see page-layout-switchable; for custom region names, see layout.
@mixin page-layout($variant: default, $collapse-at: md); body { @include m.page-layout(sidebar-left); } header { @include m.page-header; } aside { @include m.page-sidebar; } main { @include m.page-main; } footer { @include m.page-footer; }
layout
Build any page layout from your own region names. Single-column stack or rows of columns — the engine guarantees a valid rectangular grid-template-areas. A row with fewer names than the widest row stretches its last region to fill (so a single-name row becomes a full-width band). Accepts $gap, $tracks for custom column tracks, and $wire to auto-place children by [data-area].
@mixin layout($rows...); // single-column stack .app { @include m.layout(nav, body, foot); } // rows of columns .dashboard { @include m.layout((header), (sidebar main), (footer)); } // custom column tracks + auto-place via [data-area] .app-shell { @include m.layout((nav), (side body), (foot), $tracks: 16rem 1fr, $wire: true); }
named-layout
Reusable named layout from the built-in registry (default, sidebar-left, sidebar-right, holy-grail). Forwards $gap, $tracks, $wire to layout. Use when you want a preset by name with the option to auto-place children by [data-area].
@mixin named-layout($name, $gap: 4, $tracks: null, $wire: null); .app { @include m.named-layout(holy-grail, $wire: true); }
area
Place a child element into a named grid region. Equivalent to writing grid-area: <name> by hand.
@mixin area($name); .sidebar { @include m.area(sidebar); }
page-layout-switchable
Runtime layout switching with zero recompile. Drop on body; toggle <body data-layout="..."> at runtime (e.g. document.body.dataset.layout = 'holy-grail'). All four built-in layouts plus auto mobile collapse, baked into one selector-based rule per layout. Selector-based (not custom properties) because Sass strips quotes from grid-template-areas when interpolated into a custom prop.
@mixin page-layout-switchable($collapse-at: md); body { @include m.page-layout-switchable; } // HTML: <body data-layout='sidebar-left'> // JS: document.body.dataset.layout = 'holy-grail';
section
Vertical page section with consistent top/bottom padding.
@mixin section($py: 8, $px: null); section { @include m.section($py: 9); }
inset, inset-x, inset-y, squish
Padding helpers. inset applies even padding on all sides, inset-x/inset-y split axes, squish takes a Y/X pair.
@mixin inset($size: 4); @mixin inset-x($size: 4); @mixin inset-y($size: 4); @mixin squish($y: 2, $x: 4);
divider / divider-vertical
Horizontal or vertical divider with token-aware spacing.
above
below
@mixin divider($color: border-default, $spacing: 4); @mixin divider-vertical($color: border-default, $spacing: 4);
Breakpoints & container queries
Responsive at viewport and component level. Breakpoint mixins take a token (sm, md, lg, xl,2xl) or a literal width. Container-query mixins pair with container to make components respond to their own width.
media, media-down, media-between
Viewport media queries — min-width, max-width, and ranged.
@mixin media($size); @mixin media-down($size); @mixin media-between($min, $max); .hero { font-size: 2rem; @include m.media(md) { font-size: 3rem; } }
Alias mixins
Convenience aliases for common breakpoint ranges: mobile-only, tablet, tablet-only, desktop, wide.
.nav { @include m.mobile-only { display: none; } @include m.desktop { display: flex; } }
container, contain, contain-down, contain-between
Container queries. Name the container with container, then query it with contain variants. (Renamed from cq / cq-down / cq-between in v0.8 to read better; the namer container kept its name.)
@mixin container($name: null, $type: inline-size); @mixin contain($size, $name: null); @mixin contain-down($size, $name: null); @mixin contain-between($min, $max, $name: null); .card { @include m.container(card); @include m.contain(md, card) { display: grid; } }
Print / PDF
Turn any page into a faithful PDF with nothing but a print stylesheet — zero JS, no library, no server. The page is the PDF source, and the browser's native Print → “Save as PDF” is the generator. Full walkthrough in the print-to-pdf recipe. See it work at the print-to-pdf example — a real invoice you Ctrl+P: the chrome drops, the invoice fills the sheet, links print as full URLs, and a build-time, print-only QR code links the paper back to the live page.
print-base
Page-level defaults, on by default. Include once at the stylesheet root (it emits @page, which is invalid nested in a selector). Sets the page box, collapses animations to their final frame so nothing prints invisible, and emits the print variable control plane. It does not force opacity: 1 / transform: none — deliberate translucency and rotation survive.
In @media print it always sets color-scheme: light, so paired light-dark() themes print their light branch for free. Four opt-in flags (all default off) go further: $legible darkens the body-text tokens (--ink, --muted…) so dark-only themes like Terminal stay readable on white — code blocks and accents untouched; $link-urls prints each link’s destination via attr(href); $link-origin prepends an origin to internal links so they resolve to full URLs on paper; $page-numbers puts counter(page) in the @page bottom-center margin box.
@mixin print-base($freeze-animations: true, $size: letter, $margin: 0.5in, $legible: false, $link-urls: false, $link-origin: null, $page-numbers: false); @include m.print-base; // at root @include m.print-base($legible: true, $link-urls: true, $link-origin: 'https://example.com', $page-numbers: true);
print, print-hidden, print-only
print wraps a bare @media print block so an override sits next to the rule it changes. print-hidden drops chrome on paper (the “hide the nav” case); print-only reveals paper-only content (an inline URL footer, a “printed on” stamp).
.site-nav { @include m.print-hidden; } .url-foot { @include m.print-only; } .subtitle { @include m.print { color: m.color(text-secondary); } }
Why these emit !important. cia avoids it everywhere else — the print layer is a deliberate exception. @media contributes no specificity, so print-hidden produces a rule with exactly the specificity of the selector you included it in. A later declaration at equal specificity wins, in print too — and it is usually not even yours, but a utility class or a component library cia cannot see. Measured in a browser: with !important the element hides; without it, it prints anyway. The failure is silent and paper-only.
@layer would be worse here. Layered CSS always loses to unlayered CSS, so a layered print rule would lose to any consumer stylesheet that isn’t layered — which is most of them. (!important also inverts layer order, so the two don’t compose as you’d expect.) cia ships unlayered by design. The blast radius stays small: 8 declarations, all inside @media print, all variable-driven — override --print-hide / --print-show to change what happens, rather than fighting the rule to win.
The variable filter system
print-base emits three custom properties that are the control plane — flip them and the output changes with no rule rewrite. --is-print is 0 on screen / 1 on paper (read it in calc(), opacity, or @container style()); --print-hide and --print-show are the display values applied to hidden / paper-only elements. Re-aim either on a single element for a per-element exception.
.legal { @include m.print-hidden; --print-hide: revert; } // keep this one on paper .cover { @include m.print { opacity: var(--is-print); } } // fades in only on paper
Geometric utilities
Pure functions for sizes that aren't a design choice — they're geometric truth shared across Figma, the design system, and the codebase. NOT themeable; consumers don't tune the 4px grid. Distinct from m.space() which IS themeable (consumer can re-tune the spacing scale per theme).
Both functions return rem (so user zoom + browser default font-size still scale) and inline at the call site — they emit nothing on their own. The functions exist so AI tools (MCP resolve_size, Figma → Code) have a deterministic surface from design intent to cia code.
grid
Explicit geometric size on cia's 4px coordinate system. n is the step count (n × 0.25rem). Use for icon widths, control heights, fixed dimensions that must align with the design grid.
@function grid($n, $base: 0.25rem); .icon-sm { width: m.grid(4); } // 16px .control-md { height: m.grid(10); } // 40px .avatar-xl { inline-size: m.grid(20); }// 80px
px
Raw pixel value converted to rem, intentionally off-grid. Use for the rare one-off value that doesn't fit any cia scale — a 17px hero margin, a 33px badge offset. Prefer m.grid() / m.space() / the typed token functions when they fit — m.px() is the escape hatch, not the default.
@function px($value); .hero { margin-block-start: m.px(33); } // 2.0625rem .badge { margin-inline-end: m.px(17); } // 1.0625rem
grid-from-px
Snap a px value to the nearest cia grid step. For tooling (Figma → Code, MCP, codegen) — pure math, no rendering output. AI agents call this to map a design's px value to the cia step, then emit m.grid(step) in generated code.
@function grid-from-px($px, $base-px: 4); // Returns the step number (integer). Compose with m.grid() // to emit the rem value, e.g. m.grid( m.grid-from-px(24) ) // // m.grid-from-px(24) → 6 // m.grid-from-px(17) → 4 (rounds to nearest step) // m.grid-from-px(100) → 25
Which function should I use?
The decision tree for picking the right size mechanism in cia:
- color / type / radius / shadow / motion → themed tokens:
m.color(),m.font-size(),m.radius(),m.shadow(),m.duration() - margin / padding / gap →
m.space(n)(themeable spacing scale) - explicit geometric size on the grid →
m.grid(n)(4px grid, not themed) - off-grid pixel value (rare) →
m.px(value)(raw rem conversion) - component →
m.btn()/m.modal()/ etc., overriding parameters as needed
Full decision tree with composition fallbacks: /docs/composition.
Typography
All typography is token-driven. font composes weight, style, size, line-height and letter-spacing in one mixin; type pulls from the named scale (display,heading-1…heading-4, body,body-sm, caption, overline).
font
Sets weight, style, size, line-height, letter-spacing from one call. Size/lh/ls accept a token name or a literal value. Pass $family to set a font-family — if it's a name registered via font-load, the registered fallback is auto-applied; if it's a CSS-native value (with comma or var()), it passes through; if it's a single-word name that isn't loaded, the build fails with @error so typos surface at compile time.
@mixin font($type: reg, $size: null, $lh: null, $ls: null, $family: null); .lede { @include m.font(medium, $size: 4, $lh: 2); }
font-load & font-load-local
Two lines to add any custom font: declare the slug on :root in a global stylesheet, then consume from any component. font-load handles the @import url(...) plumbing for hosted fonts; font-load-local wraps @font-face for self-hosted. Both are called from a global Sass file (not .module.scss).
// --- 1) Declare on :root in your global stylesheet --- // globals.css :root { --font-meme: 'Helvetica Neue', Helvetica, Arial, sans-serif; } // --- 2) Use from any component --- .logo { @include m.font($lh: 0.95, $family: meme); } .stamp { font-family: var(--font-meme); } // --- Override anywhere — set the variable in a tighter scope --- .landing-page { --font-meme: 'Caveat', cursive; } // --- Hosted font (Google / CDN) — call from a global .scss file --- // src/styles/fonts.scss (imported from layout.tsx) @include m.font-face('Pacifico', 'https://fonts.googleapis.com/css2?family=Pacifico'); // → emits @import url(...) // then declare the variable in globals.css and consume like step 2 // --- Self-hosted font (woff2 / ttf) --- @include m.font-face-local('Untitled Sans', '/fonts/UntitledSans.woff2');
Tip: m.font($family: meme) emits font-family: var(--font-meme). The slug is just a CSS variable name — no Sass-side registry, no validation. As long as --font-meme is declared somewhere in scope, the browser resolves it.
type
Applies a named type-scale preset: size + weight + line-height + letter-spacing in one include.
@mixin type($preset); h1 { @include m.type(heading-1); } .label { @include m.type(overline); }
truncate
Single-line ellipsis by default, or multi-line clamp with $lines.
@mixin truncate($lines: 1); .headline { @include m.truncate(2); }
Colour, borders & effects
These wrap raw theme values in runtime-override-capable custom properties, so a consumer can re-skin a single site without rebuilding the library.
border
Applies a border on all sides, one side, or a list of sides, with token-aware colour.
@mixin border($width: 1px, $style: solid, $color: border-default, $sides: all); .panel { @include m.border($sides: (top, bottom)); }
elevation
Applies a theme-driven shadow level (0–5).
@mixin elevation($level: 2); .card { @include m.elevation(3); }
Interactive states
Focus ring, hover transitions, disabled state, and a composite interactive mixin that wires hover, active and disabled in one call.
focus-ring
Accessible focus-visible ring using border-focus by default.
@mixin focus-ring($color: border-focus, $width: 3px, $offset: 0); .btn { @include m.focus-ring; }
hover
One-property transition + hover change in a single line.
@mixin hover($prop, $value, $speed: fast); .link { @include m.hover(color, m.color(action-primary-hover)); }
interactive
Composite: transitions background-color, applies hover + active backgrounds, and disables the element when disabled.
@mixin interactive($bg-hover: interactive-hover, $bg-active: interactive-active); .row { @include m.interactive; }
transition
Variadic — pass any number of CSS properties plus an optional speed (instant/fast/normal/slow/slower) and easing token (smooth/bounce/etc.). Respects prefers-reduced-motion.
@mixin transition($props...); .btn { @include m.transition(background-color, color, fast, smooth); }
disabled
Standard disabled styling — dimmed, not-allowed cursor, pointer events off.
@mixin disabled($opacity: 0.5);
sr-only
Visually hide an element while keeping it available to screen readers.
@mixin sr-only; .skip-link { @include m.sr-only; }
Resets
Strip user-agent styling from common elements.
@mixin button-reset; // appearance, background, border, padding, cursor @mixin list-reset; // list-style + margin + padding @mixin header-reset; // h1..h6 within scope @mixin form-reset; // input/select/textarea full-width
Animation
Keyframes are declared once and referenced by name. All animation mixins respect prefers-reduced-motion.
animate
Trigger a named animation with configurable speed, delay, iteration and timing. Names live in _animations.scss (fade-in, fade-out, slide-up, slide-down, spin, pulse, shimmer, etc.).
@mixin animate( $name, $speed: normal, $delay: 0s, $iteration: 1, $fill: both, $timing: var(--ease, cubic-bezier(0.33, 0.66, 0.33, 1)) ); .modal { @include m.animate(slide-up); } .spinner { @include m.animate(spin, $iteration: infinite, $timing: linear); }
animate-on
Interaction-triggered animations. Events: hover, focus, active. Effects: lift, glow, press, fade.
@mixin animate-on($event: hover, $effect: lift); .card { @include m.animate-on(hover, lift); }
Shortcuts
Pre-baked animation helpers that inject the keyframes in the same call.
@mixin spin($duration: 1s); @mixin pulse($duration: 2s); @mixin fade-in($duration: normal); @mixin slide-up($duration: normal, $distance: 1rem);
Icons
SVG and Font Awesome helpers. Prefer svg for inline icon styling, svg-bg for background-image masks, svg-text for inline-text alignment. Font Awesome mixins require fa-load once at the root to inject the shared @font-face rules.
@mixin svg(...); @mixin svg-bg(...); @mixin svg-text(...); @mixin fa-load; @mixin fa($name); @mixin fa-icon($name); @mixin fa-text($name); @mixin fa-spin($name, $size, $style); :root { @include m.fa-load; } .icon-check { @include m.fa-icon(check); }
Component mixins
Composite mixins live in scss/components/*.scss and compose the atomic mixins above into real UI primitives. Every base mixin wraps its override-controlled properties in var(--<key>, <default>), so a theme can tweak padding, radius, shadow or colour without a rebuild.
Buttons
From components/buttons. Import as @use '.../components/buttons' as b;.
@mixin btn-base($py: 1, $px: 4, $r: md, $font-weight: medium, $font-size: null); @mixin btn($variant, $bg, $bg-hover, $bg-active, $color, $border, $args...); // $variant: primary | secondary | outline | ghost | info | success | warning | error | disabled @mixin btn-icon($size: 2.5rem, $r: md); // Author your own class — variant is a mixin arg, not a --modifier suffix .hero-cta { @include b.btn(primary); } .checkout-cancel { @include b.btn(outline); }
Data display — cards, lists, tables, avatars
From components/data.
Warm paper
Card-base composes padding, radius, shadow and surface colour.
Themable
Swap the theme — every card re-skins without markup changes.
@mixin table-base($striped: false, $hover: false, $bordered: false, $compact: false); @mixin table-responsive; @mixin card-base($p: 4, $r: lg, $shadow: 1, $bg: surface-default); @mixin card-header($pb: 2); @mixin card-footer($pt: 2); @mixin card-interactive; @mixin list-base($gap: 0, $dividers: false); @mixin list-item($py: 2, $px: 4, $interactive: false); @mixin avatar($size: 2.5rem, $r: full); @mixin avatar-placeholder($size, $r, $bg, $color); @mixin avatar-group($overlap: -0.5rem);
Feedback — alerts, toasts, badges, tags, progress
From components/feedback. Status-coloured variants read status-* tokens.
@mixin alert-base($py: 2, $px: 4, $r: md, $border-width: 1px); @mixin alert($status: info, $py: 2, $px: 4, $r: md); @mixin toast-base($py: 2, $px: 4, $r: lg, $shadow: 3); @mixin badge-base($py, $px, $r: full, $font-size: 1); @mixin badge($status: info); @mixin tag($py: 2xs, $px: 2, $r: md, $font-size: 2, $removable: false); @mixin progress-track-base($height, $r: full, $bg: surface-muted); @mixin progress-fill-base($color: action-primary-default); @mixin progress($height, $r: full, $bg: surface-muted, $fill: action-primary-default); @mixin spinner(...); @mixin skeleton(...);
Forms — inputs, selects, checks, radios, switches, sliders
From components/forms. All form primitives share the same focus treatment and disabled contract.
@mixin input-base($py: 1, $px: 2, $r: md, $border-width: 1px, $bg, $border-color); @mixin select-base(...); @mixin textarea-base(...); @mixin check-base($size: 1.125rem, $r: sm, $color: action-primary-default); @mixin radio-base($size: 1.125rem, $color: action-primary-default); @mixin switch-base(...); @mixin slider-base(...); @mixin label-base($size: 2, $weight: medium, $color: text-primary); @mixin form-layout($columns: 1, $gap: 4); @mixin form-group($gap: 1, $direction: column); @mixin form-row($gap: 2, $align: center); @mixin form-help($color: text-muted); @mixin form-error;
Navigation — navbar, nav, breadcrumb, tabs, pagination, mobile toolkit
From components/navigation. The mobile toolkit — hamburger, drawer, sheet, dock — is zero-JS on the native Popover API: the browser manages aria-expanded, Esc and light-dismiss. The full playbook is /docs/mobile.
@mixin navbar-base(...); @mixin navbar-brand($gap: 2); @mixin navbar-nav($gap: 1); @mixin navbar-link($py: 1, $px: 2, $r: md); @mixin nav-base($direction: row, $gap: 1); @mixin breadcrumb($gap: 1, $separator: "/"); @mixin tabs-base($gap: 0, $border: true); @mixin tab-item($py: 2, $px: 4, $active-color: action-primary-default); @mixin pagination($gap: 2xs); @mixin pagination-item($size: 2.25rem, $r: md); // mobile toolkit — zero-JS, native Popover API @mixin hamburger($color: text-primary, $bar-width: 1.375rem, $bar-height: 2px, $gap: 5px, $target: 44px); @mixin hamburger-open; @mixin drawer($side: end, $size: 20rem, $bg: surface-default, $backdrop: rgba(0, 0, 0, 0.4), $p: 5, $shadow: lg); @mixin sheet($size: auto, $max: 72dvh, $bg: surface-default, $backdrop: rgba(0, 0, 0, 0.4), $p: 5, $r: xl); @mixin dock($slots: 3, $bg: surface-default); @mixin dock-item($accent: action-primary-default);
Overlays — modals, tooltips, popovers, dropdowns
From components/overlay. tooltip and dropdown wrap their base mixins with native [popover] semantics — pair with a popovertarget trigger and the browser handles open, light-dismiss and Escape with zero JS. dropdown also guards its own closed state, re-asserting display: flex only under :popover-open so the menu never renders permanently open.
@mixin modal-backdrop($bg: rgba(0, 0, 0, 0.5)); @mixin modal-base($p: 5, $r: xl, $shadow: 5, $max-width: 500px); @mixin modal-header($pb: 2); @mixin modal-footer($pt: 2); @mixin tooltip-base($py, $px, $r: md, $bg, $color); @mixin tooltip; // tooltip-base + [popover] semantics @mixin popover-base($p: 4, $r: lg, $shadow: 3, $max-width: 320px); @mixin dropdown-menu($py: 1, $r: md, $shadow: 2, $min-width: 12rem); @mixin dropdown-item($py: 1, $px: 4); @mixin dropdown-divider($spacing: 1); @mixin dropdown; // dropdown-menu + [popover] semantics + closed-state guard
Writing your own mixins
When you extend the system, follow the same contract: read every visual value from a token helper (color(), space(), radius(), shadow(), font-size()), wrap override-controlled properties in var(--<key>, <default>) so themes can tweak them at runtime, and compose atomic mixins rather than duplicating their bodies.
// good — token-driven, override-capable @mixin note-base($p: 4, $r: md, $bg: surface-muted) { padding: var(--note-padding, #{m.space($p)}); border-radius: var(--note-radius, #{m.radius($r)}); background: m.color($bg); @include m.border($sides: left, $color: border-focus); }
The full contributor guide — naming conventions, parameter order, how to add a mixin to the barrel — lives in CONTRIBUTING.md alongside the component authoring guide.
Full index
Every public mixin at a glance. Jump to the section above for usage examples.
- Layout:
flex,flow,flow-switchable,pad,pad-x,pad-y,pad-asym,wrap,grid,subgrid,page-layout,page-header,page-main,page-footer,page-sidebar,page-nav,page-aside,layout,named-layout,area,page-layout-switchable,stack,cluster,switcher,cover,frame,sidebar,toolbar,wizard-shell,section,divider,divider-vertical - Geometric utilities (functions):
grid,px,grid-from-px - Breakpoints:
media,media-down,media-between,mobile-only,tablet,tablet-only,desktop,wide - Container queries:
container,contain,contain-down,contain-between - Print / PDF:
print,print-base,print-hidden,print-only(+--is-print/--print-hide/--print-showvars) - Typography:
font,type,truncate - Borders & effects:
border,elevation - Interactive:
focus-ring,hover,interactive,transition,disabled,sr-only - Resets:
button-reset,list-reset,header-reset,form-reset - Animation:
animate,animate-on,spin,pulse,fade-in,slide-up - Icons:
svg,svg-bg,svg-text,fa-load,fa,fa-icon,fa-text,fa-spin - Buttons:
btn-base,btn,btn-icon(variantsprimary/secondary/outline/ghost/ status are parameters tobtn(), not separate mixins) - Data:
table-base,table-responsive,card-base,card-header,card-footer,card-interactive,list-base,list-item,avatar,avatar-placeholder,avatar-group - Feedback:
alert-base,alert,toast-base,tag,progress,progress-track-base,progress-fill-base,spinner,skeleton - Data:
table-base,card-base,badge-base,badge - Forms:
input-base,select-base,textarea-base,check-base,radio-base,switch-base,slider-base,label-base,form-layout,form-group,form-row,form-help,form-error - Navigation:
navbar-base,navbar-brand,navbar-nav,navbar-link,nav-base,breadcrumb,tabs-base,tab-item,pagination,pagination-item,hamburger,hamburger-open,drawer,sheet,dock,dock-item - Overlays:
modal-backdrop,modal-base,modal-header,modal-footer,tooltip-base,tooltip,popover-base,dropdown-menu,dropdown-item,dropdown-divider,dropdown