# mCSS: full reference > mCSS is a modern CSS framework and component library for websites: real CSS, real markup, zero build step, built on native cascade layers. It is not a dependency; you copy the files into your project and own them. This file concatenates every docs and components page. A per-page index is at /llms.txt. # Browser Support > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/browser-support **mCSS targets [Baseline](https://web.dev/baseline) 2024**. It uses cascade layers (`@layer`), nesting, `:has()`, and other modern CSS features natively, with no polyfills. In practice that means the versions of Chrome, Edge, Firefox, and Safari that shipped late 2024 and after: | Browser | Version | | -------------- | ------- | | Chrome | 130+ | | Edge | 130+ | | Firefox | 132+ | | Safari (+ iOS) | 18.2+ | If you install mCSS with the [PostCSS option](/docs/installation#option-3-compiled-via-postcss), it is possible to modify mCSS browser support via the `.browserslistrc` file. Using a different target, `postcss-preset-env` will start polyfilling the newer features. But it's not recommended: it will create file bloat and it's not foolproof. Some features can break in the process. (See [this blog post](/blog/postcss-setup-for-mcss) for details on what can break when changing the default browser support.) --- # Components > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/components Components in a mCSS project come in two kinds: - **Built-in components**: a [library of ready-made components](/components/start) built on top of mCSS. Like the rest of the framework, you copy the pieces you want into your project and own the code. It ships separately (`mcss.components.css`), so skip it entirely if you don't need it. - **Your own components**: mCSS is designed for building your own, one `component.*.css` file per block, in your own CSS folder. The [class syntax](/docs/start#mcss-classes-syntax) and [component conventions](/docs/start#components) from Getting Started are all you need. The two kinds mix freely: this very site's pages are built from library components, site components, and the occasional page override. ## Marketing template A complete marketing one-pager built with mCSS and its built-in Astro components: no site CSS, no custom components, no JavaScript beyond what the components ship with. **[Open the live template](/templates/marketing)**, and use the switcher in its bottom corner to swap between the default theme and the [wireframe theme](/docs/default-theme#full-themes). The page uses the [base layout scaffold](/docs/layout#layouts) and the following components, from top to bottom: - [Banner](/components/banner) - [Header](/components/header) (sticky, with mobile menu) - [Hero](/components/hero) (full variant on a gradient) - [Section](/components/section) with a [feature grid](/components/featuregrid) ([Tiles](/components/tiles) + FeatureItems), a filled Section of [Testimonials](/components/testimonial) on the grid - [Pricing](/components/pricing) - [FAQ](/components/faq) with exclusive-open items - Closing-CTA [Section](/components/section) - [Footer](/components/footer) ### Using it Copy [`marketing.astro`][srcTemplate] into your own `src/pages/` (as `index.astro`, most likely) and definitely swap the copy ;) Everything it needs is listed below. If you copied the whole framework and component library per [Getting started](/docs/start), you should be good to go. Two blocks in the file exist for the docs demo and are safe to remove: - The theme switcher at the bottom (marked with a comment) is just for the demo. - The inline `@layer` statement at the top of `` sets the cascade-layer order before any stylesheet loads. It's needed when a theme is loaded as a separate ``, which we do here to power the theme switcher.
| What | Files | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The page | `src/pages/templates/marketing.astro` ([source][srcTemplate]) | | Astro components | `Banner`, `Header`, `Hero`, `Section`, `Tiles`, `FeatureItem`, `Testimonial`, `Avatar` (used by Testimonial), `PricingCard`, `Faq`, `FaqItem`, `Footer` ([source][srcComponents]) | | mCSS | The framework entries `mcss.css`, `mcss.components.css`, and `theme.default.css` cover every style used ([source][srcFramework]) | | Icons | `menu`, `x`, `check`, `circle-check`, `moon-star`, `external-link`, `rotate-ccw`, `mail`, `heart` ([source][srcIcons]) | | Scripts | `src/scripts/utilities.js` (the Header's scroll throttle) ([source][srcScripts]) |
[srcTemplate]: https://github.com/minimaldesign/mCSS/blob/main/src/pages/templates/marketing.astro [srcComponents]: https://github.com/minimaldesign/mCSS/tree/main/src/components [srcFramework]: https://github.com/minimaldesign/mCSS/tree/main/src/styles/framework [srcIcons]: https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons [srcScripts]: https://github.com/minimaldesign/mCSS/blob/main/src/scripts/utilities.js ### Useful recipes Even if you don't use the whole page, three patterns in it are useful to know: - **The closing CTA** is not a component; it is a `Section` with `variant="primary"` and a `.section_actions` row (see the [Section recipes](/components/section)). - **The pricing row** is three [PricingCards](/components/pricing) on the plain [grid](/docs/layout#grid) (`col="1" col-md="3"`), with `highlighted` on the recommended tier. - **Exclusive-open FAQ** items share a `name`, so the browser closes one when another opens. Native `
`, no JavaScript. --- # Default HTML > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/default-html
| File name | Source | | ---------------- | ------------- | | `base.reset.css` | [Github][src] |
The mCSS reset is kept to a minimum. It does the following: - It sets the `box-sizing` to `border-box` - It removes the default `margin` on all elements - It sets the document's `min-height` to 100%. The rest of the defaults that would traditionally be included in a "reset" or "normalize" file are handled by the `elements.*.css` files. Refer to the [mCSS File structure][structure] section of _Getting Started_ for more details. You can find an example of all HTML elements default styling in the sections [below](#sectioning). [structure]: /docs/start#mcss-file-structure [src]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/base.reset.css ## Sectioning
| File name | Source | | ------------------------- | ----------------------- | | `elements.sectioning.css` | [Github][srcSectioning] |
[srcSectioning]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.sectioning.css Technically, `address` and `h1`–`h6` are [content sectioning elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#content_sectioning), like `header`, `section`, etc. But mCSS approach is to prioritize intuitiveness and ease of use rather than technical correctness, so they're included here with the rest of the text elements. ## Text
| File name | Source | | ------------------------- | ----------------------- | | `elements.text.css` | [Github][srcText] |
[srcText]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.text.css ### Headings Heading sizes are fluid via `clamp()` set on `--heading-font-size-h1`—`--heading-font-size-h6` tokens: each interpolates from a minimum size (at 480px viewports and below) to a maximum (from 1024px up), so the type scale tightens on small screens without media queries. The preferred value mixes `rem` with `vw`, which keeps browser text zoom working. The tokens live in `theme.default.ui.css` with the rest of the heading family (`--heading-spacing-*`, `--heading-line-height-*`), mapping each level onto the [type scale](/docs/default-theme#font-size); override any of them in your theme to change how a heading level scales.
| Token | Min (≤480px) | Max (≥1024px) | | ---------------------- | ----------------------- | ----------------------- | | `heading-font-size-h1` | `display-md` (2.074rem) | `display-xl` (2.986rem) | | `heading-font-size-h2` | `display-sm` (1.728rem) | `display-lg` (2.488rem) | | `heading-font-size-h3` | `text-xl` (1.44rem) | `display-md` (2.074rem) | | `heading-font-size-h4` | `text-lg` (1.2rem) | `display-sm` (1.728rem) | | `heading-font-size-h5` | `text-lg` (1.2rem) | `text-xl` (1.44rem) | | `heading-font-size-h6` | `text-md` (1rem) | `text-lg` (1.2rem) |

Heading One

A small paragraph of text to show the heading in context.

Heading Two

A small paragraph of text to show the heading in context.

Heading Three

A small paragraph of text to show the heading in context.

Heading Four

A small paragraph of text to show the heading in context.

Heading Five

A small paragraph of text to show the heading in context.

Heading Six

A small paragraph of text to show the heading in context.

Consecutive headings get less margin… H1 followed by a H2

H2 right after a H1

H3 right after a H2

H4 right after a H3

### Inline Elements
| Element | Example | | ---------- | ---------------------------------------------------------------------------- | | `` | This is a text link. | | `` | Abbreviation: HTML in a sentence. | | `` | The b element is only stylistically different text from normal text. | | `` | This is a citation. | | `` | This text is deleted | | `` | The dfn element indicates a definition. | | `` | This text has added emphasis. | | `` | The i element is text that is offset from the normal text. | | `` | This text is inserted. | | `` | The mark element indicates a highlight. | | `` | This is a short inline quotation. | | `` | This text has a strikethrough. | | `` | This small text is small for fine print, etc. | | `` | Strong is used to indicate strong importance. | | `` | Subscript for things like H2O. | | `` | Superscript™️. | | `` | The variable element, such as x = y. |
### Paragraphs A paragraph (from the Greek paragraphos, “to write beside” or “written beside”) is a self-contained unit of a discourse in writing dealing with a particular point or idea. A paragraph consists of one or more sentences. Though not required by the syntax of any language, paragraphs are usually an expected part of formal writing, used to organize longer prose.
### Definition list
Definition List description term
This is a definition list description details
Another description
With the related description details
### Ordered List 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item 1. List Item ### Unordered List - List Item - List Item - List Item - List Item - List Item - List Item - List Item - List Item - List Item - List Item
### Blockquotes

It would be going too far to call our modern offices sensory-deprivation chambers, but they are most certainly sensory-reduction chambers. They may not brainwash, but the elimination of sensory stimuli definitely increases focus on the task at hand, the work to be done, to the exclusion of all else. Modern offices were designed for that very purpose by people who knew what they were doing.

—Jerry Mander, Four arguments for the elimination of television

### Horizontal Rule The object of life is not to be on the side of the majority, but to escape finding oneself in the ranks of the insane.
You have power over your mind — not outside events. Realize this, and you will find strength.
### code

Keyboard input: Cmd

Inline code: <div>code</div>

Sample output: This is sample output from a computer program.

### Pre-formatted text
    P R E F O R M A T T E D T E X T !
    " # $ % & ' ( ) * + , - . / 0 1 2
    3 4 5 6 7 8 9 : ; < = > ? @ A B C
    D E F G H I J K L M N O P Q R S T
    U V W X Y Z [ \ ] ^ _ ` a b c d e
    f g h i j k l m n o p q r s t u v
    w x y z ~
  
### Address

Retro whimsical default… Because, why not?

sonny@rollins.com
+1 (212) 123 1234
## Table
| File name | Source | | -------------------- | ------------------ | | `elements.table.css` | [Github][srcTable] |
[srcTable]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.table.css
Numbers of albums recorded
Musician Instrument Albums
Sonny Rollins Saxophone 60
John Coltrane Saxophone 56
Joe Henderson Saxophone 25
Wayne Shorter Saxophone 36
Total Albums 177
## Form
| File name | Source | | -------------------- | ---------------- | | `elements.form.css` | [Github][srcForm] | [srcForm]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.form.css See [How to structure a web form](https://developer.mozilla.org/en-US/docs/Learn/Forms/How_to_structure_a_web_form) on MDN. This is just the bare minimum default styles to make any form look consistent out of the box. See the [field component](/components/field) for more. The `legend` HTML element is notoriously impractical to style. The example below uses the following workaround: ```html Example of custom fieldset legend ``` ```css fieldset { > [aria-hidden] { /* [styling goes her…] */ } } legend { /* Visually hidden */ position: absolute; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); width: 1px; height: 1px; white-space: nowrap; } ```
Example of custom fieldset legend

Select menus

Checkboxes
Radio buttons
Textareas

HTML5 inputs

Action buttons

### Meter ```html 2 out of 10 5 out of 10 8 out of 10 ```

2 out of 10

5 out of 10

8 out of 10

### Progress

70%

## Media
| File name | Source | | -------------------- | ------------------ | | `elements.media.css` | [Github][srcMedia] | [srcMedia]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.media.css
  • Plain `` element Saxophonist playing in Central Park
  • `
    ` element with `` element
    Saxophonist playing in Central Park
  • `
    ` element with `` and `
    ` elements
    Saxophonist playing in Central Park
    Here is a caption for this image.
  • `
    ` element with a `` element
    Saxophonist playing in Central Park
### Audio
### Video
### Canvas canvas
### Inline Frame
### Inline SVG ## Interactive
| File name | Source | | -------------------- | ------------------ | | `elements.interactive.css` | [Github][srcInteractive] | [srcInteractive]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.interactive.css ### Details & Summary
Expand for details

Lorem ipsum dolor sit amet consectetur adipisicing elit. Cum, odio! Odio natus ullam ad quaerat, eaque necessitatibus, aliquid distinctio similique voluptatibus dicta consequuntur animi. Quaerat facilis quidem unde eos! Ipsa.

### Dialog

A random quote you might enjoy…

If you want to awaken all of humanity, then awaken all of yourself. If you want to eliminate the suffering of the world, then eliminate all that is negative in yourself. Truly the greatest gift you have to give is that of your own self-transformation.

--- # Default Theme > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/default-theme
| File name | Description | Source | | -------------------------- | ------------------------------------------- | ---------------------- | | `theme.default.css` | Default theme entry: imports the next two | [Github][srcDefault] | | `theme.default.tokens.css` | Raw tokens: colors, type, spacing, etc. | [Github][src] | | `theme.default.ui.css` | UI tokens: what elements and components use | [Github][srcUi] | | `theme.starter.css` | Use it as a start for your own theme | [Github][srcStarter] | | `theme.wireframe.css` | Full swappable theme example | [Github][srcWireframe] |
[srcDefault]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.css [src]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.tokens.css [srcUi]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css [srcStarter]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.starter.css [srcWireframe]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.wireframe.css Every design value in mCSS (colors, fonts, spacing, component styling) lives in the **default theme**. Your own theme should be imported after it and just override what it needs. **Never edit the default theme files.** Keep your design decisions in your own theme, so updating the framework is easy. ## Activating a theme Activate the default theme right after the framework, then (optionally) your own theme: ```css @import url(./framework/mcss.css); @import url(./framework/mcss.components.css); /* optional */ @import url(./framework/theme.default.css); @import url(./site/theme.starter.css); /* your theme */ ``` Theme files are **self-layered** and your theme goes in the `theme.user` layer: ```css /* theme.starter.css */ @layer theme.user { /* […] */ } ``` mCSS is set up so that, regardless of how you load your theme (`@import`, ``, or bundler), it always overrides the default theme. ## Writing your own theme Copy [`theme.starter.css`][srcStarter] and start overriding: - **Tokens first.** Most themes never need a selector: raw tokens for the palette and type, UI tokens for general UI changes, and component tokens for details. - **Style rules only for what tokens can't express**: pseudo-elements, `nth-child` rhythm, font smoothing, `text-wrap`, etc. Your theme file is the one place where overriding framework classes and HTML elements from other files is ok. - **Check tokens before writing a style rule.** The page is already painted from `--body-background-color`, `--text-color`, and `--text`: set those instead of writing a `body` rule for background, color, or font. - **No `!important`.** It will break the layer system. And if you need `!important`, it's a sign you're doing something wrong. ## Full themes A theme can also be a complete, distributable style. mCSS ships with the [`theme.wireframe.css`][srcWireframe] example. (Flip the switcher on the [marketing template](/templates/marketing) to see it live.) A full theme is an entry that imports the default theme itself and overrides on top, so you activate it INSTEAD of the default: ```css @import url(./framework/mcss.css); /* theme.default.css is swapped */ @import url(./framework/theme.wireframe.css); ``` The wireframe file is a good read if you're planning a full theme. Token overrides do most of the work (grayscale palette, handwriting font stack, "paper cutout" shadows), a few style rules cover the rest (wavy underlines, tilted cards), and it's deliberate about what it leaves alone: the focus ring stays crisp, and text stays high-contrast. ## Tokens Tokens are CSS custom properties, split in two files: - **Raw tokens** are named for what they **are**: raw values like colors, type sizes, spacing, radii, etc. - **UI tokens** are named for what they're **for**: they map raw tokens to elements and components. Because components read `var(--ui-border-color)` instead of a raw hex value, one override can have three levels of blast radius: ```css @layer theme.user { :root { /* Whole palette shifts */ --base-950: #1a1208; /* Every bordered surface at once */ --ui-border-color: light-dark(var(--base-300), var(--base-600)); /* Single component override */ --bt-border-radius: 0; } } ``` That's why small theme files can go far with a handful of token overrides. ## How to use tokens When you start a new project, put your design's raw token values in your own theme: your `base` and `primary` colors, your font stacks, and whatever else your design calls for. The defaults are sensible and things like spacing might not need to change. Then continue overriding UI tokens provided by mCSS as you go, and creating your own when needed. Do not add hard-coded "magic numbers" anywhere else but in your theme's tokens. ## UI tokens UI tokens are named for what they're for: semantic aliases (`--ui-border-color`), element defaults (`--heading-font`, `--input-padding`, etc.), and every component's defaults (`--badge-color`, `--card-spacing`, etc.). A UI token usually takes another token for value: a UI token when the meaning is shared (`--input-border-color` is `--ui-border-color`), or a raw token when the value should stay in sync with the rest of the design (`--fieldset-padding` is `--sm3`). Hard-coded values are for the few places where that sync would be wrong: content (separator strings, icon URLs), geometry tuned to one control (`--toggle-width`), and element-relative `em` measures. For your own components, there's no hard and fast rule about which kind of token to use. If the same style comes up in several places with the same meaning, a new UI token is probably the best option. ## Raw tokens ### Color Check out [tints.dev](https://www.tints.dev) if you want to make your own palettes programmatically.
#### Base | Token | Value | Demo | | ---------- | ------- | ----------------------------------------- | | `base-0` | #fff |
| | `base-50` | #f6f7f9 |
| | `base-100` | #edeef1 |
| | `base-200` | #d6dbe1 |
| | `base-300` | #b2bbc7 |
| | `base-400` | #8897a8 |
| | `base-500` | #697a8e |
| | `base-600` | #546375 |
| | `base-700` | #4a5666 |
| | `base-800` | #3c4550 |
| | `base-900` | #353c45 |
| | `base-950` | #23282e |
|
#### Primary | Token | Value | Demo | | ------------- | ------- | -------------------------------------------- | | `primary-50` | #f0f9ff |
| | `primary-100` | #e0f2fe |
| | `primary-200` | #bae6fd |
| | `primary-300` | #7dd3fc |
| | `primary-400` | #38bdf8 |
| | `primary-500` | #0ea5e9 |
| | `primary-600` | #0284c7 |
| | `primary-700` | #0369a1 |
| | `primary-800` | #075985 |
| | `primary-900` | #0c4a6e |
| | `primary-950` | #082f49 |
|
#### Feedback | Token | Value | Demo | | ----------- | ------- | ------------------------------------------ | | `yes-100` | #d5f6e8 |
| | `yes-200` | #afebd4 |
| | `yes-300` | #7adbbd |
| | `yes-400` | #46c4a0 |
| | `yes-500` | #13886d |
| | `no-100` | #ffe4e6 |
| | `no-200` | #fecdd3 |
| | `no-300` | #fda4af |
| | `no-400` | #fb7185 |
| | `no-500` | #e11d48 |
| | `maybe-100` | #fff7d6 |
| | `maybe-200` | #fff0b3 |
| | `maybe-300` | #ffd64a |
| | `maybe-400` | #ffc220 |
| | `maybe-500` | #f9a007 |
| #### Highlight The text marker color, consumed by the `--marker-color` UI token (what `` uses). Override `--highlight-500` in a theme to restyle every highlight. | Token | Value | Demo | | --------------- | ------- | --------------------------------------------------------------------------- | | `highlight-500` | #ffee00 |
|
### Dimension Dimension tokens can be used anywhere you need to set `margin`, `padding`, `width`, `height`, etc.
| Token | Value | Demo | | ------- | ----- | ------------------------------------ | | `xs1` | 4px |
| | `xs2` | 8px |
| | `xs3` | 12px |
| | `sm1` | 16px |
| | `sm2` | 20px |
| | `sm3` | 24px |
| | `md1` | 28px |
| | `md2` | 32px |
| | `md3` | 36px |
| | `lg1` | 40px |
| | `lg2` | 44px |
| | `lg3` | 48px |
| | `xl1` | 56px |
| | `xl2` | 64px |
| | `xl3` | 80px |
| | `xxl1` | 96px |
| | `xxl2` | 112px |
| | `xxl3` | 128px |
| | `mega1` | 160px |
| | `mega2` | 192px |
| | `mega3` | 224px |
| | `giga1` | 256px |
| | `giga2` | 288px |
| | `giga3` | 320px |
| | `tera1` | 384px |
| | `tera2` | 480px |
| | `tera3` | 520px |
|
### Aspect ratio | Token | Value | Demo | | --------------- | ------- | ------------------------------------------ | | `ar-square` | 1 |
| | `ar-landscape` | 4/3 |
| | `ar-portrait` | 3/4 |
| | `ar-widescreen` | 16/9 |
| | `ar-golden` | 1.618/1 |
| ### Typography #### Font stack There are 3 font stacks set up by default for the `font-family` CSS property. | Token | Value | | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `text` | ui-sans-serif, system-ui, sans-serif | | `display` | Avenir, Montserrat, Corbel, URW Gothic, source-sans-pro, ui-sans-serif, sans-serif | | `mono` | Dank Mono, Inconsolata, Fira Mono, SF Mono, Monaco, Droid Sans Mono, Source Code Pro, Cascadia Code, Menlo, Consolas, DejaVu Sans Mono, ui-monospace, monospace | These stacks are optimized for fonts available on device, to maximize for speed and alleviate any layout shifts or flashes. You can find more stacks at [Modern Font Stacks](https://modernfontstacks.com). If you'd like to bring in your own fonts or [Google fonts](https://fonts.google.com), you can override the `display` and `text` tokens inside your theme file. In case you're curious, `ui-sans-serif` is the system's sans-serif UI font, `system-ui` is the system's UI font whether serif or sans-serif, and `sans-serif` is the browser default. #### Font size Check out [typescale.com](https://typescale.com) if you need help creating your own sizes.
| Token | Value | Demo | | -------------- | -------- | ---------------------------------------------- | | `text-xs` | 0.694rem |
Sample demo text
| | `text-sm` | 0.833rem |
Sample demo text
| | `text-md` | 1rem |
Sample demo text
| | `text-lg` | 1.2rem |
Sample demo text
| | `text-xl` | 1.44rem |
Sample demo text
| | `display-sm` | 1.728rem |
Sample demo text
| | `display-md` | 2.074rem |
Sample demo text
| | `display-lg` | 2.488rem |
Sample demo text
| | `display-xl` | 2.986rem |
Sample demo text
| | `display-mega` | 3.583rem |
Demo text
| | `display-giga` | 4.299rem |
Demo text
|
The heading font sizes (`--heading-font-size-h1`…`-h6`) build on this scale but are [UI tokens](#ui-tokens); they're documented with [the headings themselves](/docs/default-html#headings). #### Font weight | Token | Value | Demo | | ------------- | ----- | ---------------------------------------------------- | | `extra-light` | 200 |
Sample demo text
| | `light` | 300 |
Sample demo text
| | `book` | 400 |
Sample demo text
| | `semi-bold` | 600 |
Sample demo text
| | `bold` | 700 |
Sample demo text
| | `black` | 900 |
Sample demo text
| #### Letter spacing | Token | Value | Demo | | -------------- | ------- | ------------------------------------------------ | | `tracking-sm` | -0.05em |
Sample demo text
| | `tracking-md` | 0.025em |
Sample demo text
| | `tracking-lg` | 0.05em |
Sample demo text
| | `tracking-xl` | 0.075em |
Sample demo text
| | `tracking-xxl` | 0.15em |
Sample demo text
| #### Line height | Token | Value | Demo | | ------------- | ----- | ------------------------------------------------------------------- | | `leading-xs` | 1 |
Sample demo text
| | `leading-sm` | 1.15 |
Sample demo text
| | `leading-md` | 1.375 |
Sample demo text
| | `leading-lg` | 1.5 |
Sample demo text
| | `leading-xl` | 1.75 |
Sample demo text
| | `leading-xxl` | 2 |
Sample demo text
| ### Borders
#### Border radius | Token | Value | Demo | | -------------- | ----- | ----------- | | `radius-sm` | 3px |
| | `radius-md` | 5px |
| | `radius-lg` | 8px |
| | `radius-xl` | 12px |
| | `radius-xxl` | 16px |
| | `radius-round` | 1e5px |
|
#### Border width | Token | Value | Demo | | ------------ | ----- | ----------- | | `border-sm` | 1px |
| | `border-md` | 2px |
| | `border-lg` | 4px |
| | `border-xl` | 8px |
| | `border-xxl` | 12px |
|
### Drop Shadow It wouldn't be super useful to list the values here. You can look at them [on Github][githubShadows] if you're curious. [githubShadows]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.tokens.css#L217
| Token | Demo | | ------------ | ----------- | | `shadow-sm` |
| | `shadow-md` |
| | `shadow-lg` |
| | `shadow-xl` |
| | `shadow-xxl` |
|
### Opacity | Token | Value | Demo | | ----- | ----- | ---------------------------------------- | | `o-0` | 0 | Opacity level 0 | | `o-1` | 0.2 | Opacity level 1 | | `o-2` | 0.4 | Opacity level 2 | | `o-3` | 0.6 | Opacity level 3 | | `o-4` | 0.8 | Opacity level 4 | | `o-5` | 1 | Opacity level 5 | ### Z-index | Token | Value | | ---------- | ----------- | | `z-bottom` | -1000000000 | | `z-0` | 0 | | `z-1` | 10 | | `z-2` | 20 | | `z-3` | 30 | | `z-4` | 40 | | `z-5` | 50 | | `z-top` | 1000000000 | ### Transition | Token | Value | | ----------------- | ----------------- | | `transition` | 220ms ease-in-out | | `transition-fast` | 100ms ease-in-out | --- # Installation > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/installation ## Installation options ### Option 1: easy install Link the [framework file][dist-min] in your `` and optionally the [components styles][dist-components-min] if you want to use mCSS components: ```html ``` (Unminified versions ([framework][dist-css], [components][dist-components]) are available if you want to read the code.) ### Option 2: individual files The sweet spot between ease of use and customization options: everything is pre-processed, so there is no build step and no PostCSS. But you can pick and choose which file to include depending on what you need. Every framework and component file is available pre-processed in [`dist/css/`][dist-dir] with the `@import` index at [`dist/css/mcss.css`][dist-index]. The files are not minified so you can read and understand them. (For the smallest size possible, see the third install option.) ```html ``` Add/remove/comment out files in `mcss.css` according to what you need ([more info][mcss-layers]) and override the default styles with your own [theme][themes]. **Don't delete the `@layer` statement.** ```css /* css/mcss.css */ @layer base, elements, global, components, theme.default, theme.user, external, helpers; @import url(./base.reset.css) layer(base); /* […] */ @import url(./theme.default.css); @import url(./theme.starter.css); /* your theme */ /* […] */ @import url(./help.typography.css) layer(helpers); ``` ### Option 3: compiled via PostCSS If you want the most flexibility and full customization, this is the option for you. #### PostCSS setup You'll need three things to get PostCSS working (more details in [this blog article][postcss-post] if needed): #### `postcss-preset-env` Install [postcss-preset-env][presetEnv] as a dev dependency. ```shell npm install -D postcss postcss-preset-env ``` #### `postcss.config.cjs` Add this `postcss.config.cjs` at the root of your project. ```cjs // postcss.config.cjs const postcssPresetEnv = require("postcss-preset-env"); const config = { plugins: [ // Uncomment if you use mixins (needs `npm install -D postcss-mixins`) // require("postcss-mixins"), postcssPresetEnv({ stage: 2, features: { "cascade-layers": false, "random-function": false, }, }), ], }; module.exports = config; ``` #### `.browserslistrc` A [`.browserslistrc`][browserslistrc], at the repo's root. It sets the [compile floor](/docs/browser-support) so preset-env resolves `@custom-media` and leaves other modern features alone. ```ini # .browserslistrc baseline 2024 ``` If you already use Astro, Vite, Next, or any other bundler, that's it! Otherwise you'll need `postcss-cli` and `postcss-import` to run the build yourself (details in [blog article][postcss-post]). #### mCSS setup Once PostCSS is set up, copy [`src/styles/framework/`][framework-src] in your CSS folder. Next to it, add a folder for your own CSS. Finally, add a global import CSS file. A bare minimum setup will look like this: ```css /* global.css */ @import url(./framework/mcss.css); @import url(./framework/theme.default.css); @import url(./site/theme.starter.css); ``` A more common full featured setup: ```css /* global.css */ @import url(./framework/mcss.css); @import url(./framework/mcss.components.css); @import url(./framework/theme.default.css); @import url(./site/global.layout.css); @import url(./site/component.customComponent.css); /* etc. */ @import url(./site/page.home.css); /* etc. */ @import url(./site/theme.starter.css); ``` [browserslistrc]: https://github.com/minimaldesign/mCSS/blob/main/.browserslistrc [components]: /components/start [dist-components-min]: https://github.com/minimaldesign/mCSS/blob/main/dist/mcss.components.min.css [dist-components]: https://github.com/minimaldesign/mCSS/blob/main/dist/mcss.components.css [dist-css]: https://github.com/minimaldesign/mCSS/blob/main/dist/mcss.css [dist-dir]: https://github.com/minimaldesign/mCSS/tree/main/dist/css [dist-index]: https://github.com/minimaldesign/mCSS/blob/main/dist/css/mcss.css [dist-min]: https://github.com/minimaldesign/mCSS/blob/main/dist/mcss.min.css [framework-src]: https://github.com/minimaldesign/mCSS/tree/main/src/styles/framework [mcss-layers]: /docs/start#the-layers [postcss-post]: /blog/postcss-setup-for-mcss [presetEnv]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-preset-env [themes]: /docs/default-theme --- # Layout > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/layout Everything you need to lay out a page: a [grid](#grid), a content [wrapper](#wrap), full-page [layout scaffolds](#layouts), and the [media queries](#media-queries) they respond to. ## Grid
| File name | Source | | ----------------- | ----------------- | | `global.grid.css` | [Github][srcGrid] |
[srcGrid]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.grid.css A lightweight, flexible grid system. It uses the [CSS grid property](https://developer.mozilla.org/en-US/docs/Web/CSS/grid) under the hood, but you set up your layouts directly in your HTML. (The implementation is heavily inspired by [Raster](https://github.com/rsms/raster).) ### Syntax It's composed of 2 classes: `.grid` for the container, and `.grid_item` for each grid item. The default grid gap is defined as a custom property in [`theme.default.ui.css`](/docs/default-theme#ui-tokens) and is responsive: `16px` (`--sm1`) on small screens, stepping up to `24px` (`--sm3`) at the `--md` breakpoint (768px). You can override `--grid-column-gap` and `--grid-row-gap` globally in your own theme, or on a specific `.grid` element. A `grid-flush` modifier is available if you don't want any gap. Because `.grid` reads its gaps from those two custom properties, overriding a specific grid is just a matter of setting them on the element. Add a class of your own: ```css .photoWall { --grid-column-gap: var(--xs2); --grid-row-gap: var(--xs2); } ``` ```html
    ...
``` You define the number of columns of your grid with a custom `col` attribute and the position/width of your grid items with the custom `span` attribute. The `span` property syntax follows these 5 patterns: - `row`: the grid_item spans a full row. - `3`: the grid_item starts in the next column and spans 3 columns. - `3-5`: the grid_item starts in column 3 and ends in column 5. - `3+5`: the grid_item starts in column 3 and spans 5 columns. - `3..`: the grid_item starts in column 3 and spans the remainder of the row. Both the `col` and `span` attributes have responsive versions, which use [the "mobile first" approach][1] of the `--*-n-above` media queries. The available tiers are **`-md`, `-lg`, and `-xl`** (`col-md`, `span-lg`, etc.); other suffixes like `col-sm` or `col-xxl` don't exist and are silently ignored. Grids support up to 24 columns. (The file is generated by `src/tools/generate.global.grid.cjs`; add a tier there if you need one.) [1]: /docs/layout#recommended-responsive-setup The custom attribute will not validate. If this is important to you, you'll need to change the attributes to `data-col` and `data-span` in both the CSS and HTML. I personally prefer to use shorter, more legible attributes. Check this thread on Github if you'd like to read more than you've ever wanted to know about [custom attributes and validation](https://github.com/whatwg/html/issues/2271). ### Examples #### Different types of spans 9 column grid, with all types of spans.
2
4-5
6..
2-3
5..
2+3
6+2
row
4+2
```html
2
4-5
6..
2-3
5..
2+3
6+2
row
4+2
``` #### Responsive grid The grid layout follows a "mobile first" approach using `--*-n-above` media queries. The following example goes from 1 → 4 → 9 → 12 columns. (Resize window to see it in action.)
1
2
3
4
5
6
7
8
9
10
11
12
```html
[…]
``` ## Wrap
| File name | Source | | ----------------- | ----------------- | | `global.wrap.css` | [Github][srcWrap] |
[srcWrap]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.wrap.css The `.wrap` global class is a simple content wrapper: it centers your content on the page and adds some left/right padding on small viewports. The content column comes in three width presets, set with a modifier on the wrapper: `.wrap-sm`, `.wrap-md`, and `.wrap-lg`. A bare `.wrap` uses the `md` width (`minmax(0, 70ch)`). There is also a `.wrap_content-fullBleed` modifier for children that should extend to the full size of the viewport. See [demo](/demos/wrap). ### Syntax ```html
``` ### Settings Three responsive paddings and the content widths, set in `theme.default.ui.css`. Override them in your [theme](/docs/default-theme).
| Token | Value | Description | | ----------------- | ---------------------- | -------------------------------- | | `wrap-spacing` | `var(--sm1)` | default left/right padding | | `wrap-md-spacing` | `var(--sm3)` | left/right padding tablet and up | | `wrap-lg-spacing` | `0` | left/right padding laptop and up | | `wrap-width-sm` | `minmax(0, 55ch)` | narrow column (`.wrap-sm`) | | `wrap-width-md` | `minmax(0, 70ch)` | reading column (`.wrap-md`) | | `wrap-width-lg` | `minmax(0, 900px)` | wide column (`.wrap-lg`) | | `wrap-width` | `var(--wrap-width-md)` | width of a bare `.wrap` |
The "tablet and up" and "laptop and up" paddings follow the [responsive setup](#recommended-responsive-setup) below. ## Layouts
| File name | Source | | ------------------- | ------------------- | | `global.layout.css` | [Github][srcLayout] |
[srcLayout]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.layout.css Page-level scaffolds: the skeleton between your `body` tag and your content. Every variant extends the same `.layout` base, an app shell with a header, a stretching content area, and a sticky footer. ### Syntax ```html
``` The header and footer are optional: a bare `.layout` with only `.layout_content` still gives you a full-height page (that's what `.layout-cover` relies on). ### The layouts Each thumbnail links to a full-page demo built from labeled color blocks. Resize the demo window to see the breakpoints, sticky footer, and full-viewport behavior.
| Class | What it's for | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.layout` | The base class: header / content / footer with a sticky footer. Required by every variant, usable alone. | | `.layout-centered` | A single centered reading column (articles, blog posts, legal pages). Width capped at `--layout-content-width`. | | `.layout-sidebar` | Aside + main from the `--md` breakpoint, single column below it (aside follows source order). | | `.layout-sidebar-end` | Compose with `.layout-sidebar` to move the aside to the opposite side. | | `.layout-docs` | Documentation: nav aside + main + TOC aside (`.layout_content_aside-toc`). One column below `--md` with the asides hidden, nav + main from `--md`, TOC from `--lg`. | | `.layout-split` | Two equal full-height panes (the direct children of `.layout_content`), stacked below `--md`. Auth screens, image + content landings. | | `.layout-cover` | Full-viewport (`100dvh`) cover with content centered both ways. Landing, 404, coming-soon pages. |
`.layout-docs` hides both asides below the `--md` breakpoint. That's deliberate: docs navigation belongs in your header menu on small screens (that's how mcss.dev does it). So make sure everything in your asides is also reachable somewhere else, like the header menu. If it isn't, use `.layout-sidebar` instead: on small screens it keeps the aside on the page, stacked on top of the main content, instead of hiding it. ### Container queries Every layout main column (`.layout_content_main`) is a named [size container](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries) (`container: main / inline-size`). You can use it in your container queries: ```css @container main (width >= …); ``` ### Settings Column widths come from three tokens in `theme.default.ui.css`. `--layout-toc-width` accepts any grid track size; the `1fr` default lets the TOC resize to whatever space the main column doesn't use.
| Token | Value | Description | | ------------------------ | ------- | ---------------------------------------------------- | | `--layout-content-width` | `70ch` | main column cap (`.layout-centered`, `.layout-docs`) | | `--layout-aside-width` | `245px` | aside column (`.layout-sidebar`, `.layout-docs`) | | `--layout-toc-width` | `1fr` | TOC column (`.layout-docs`) |
Override them globally in your [theme](/docs/default-theme), or per-layout with a scoped rule. This very site uses `.layout-docs` and layers its own specifics on top ([source][srcSiteLayout]): a wider `77ch` reading column and sticky side navs. [srcSiteLayout]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/site/global.layout.css ## Prose
| File name | Source | | ------------------ | ------------------ | | `global.prose.css` | [Github][srcProse] |
[srcProse]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.prose.css The global `.prose` class can be used anywhere you need basic typography for long form text, like articles, blog posts, etc. It makes spacing between paragraphs, lists, tables, etc. consistent. This spacing can be modified in your [theme](/docs/default-theme) via the `--prose-spacing` token. The `.prose` class also adds extra styling for `ol`, `ul`, and `code` elements. ## Accessibility
| File name | Source | | ----------------- | ----------------- | | `global.a11y.css` | [Github][srcA11y] |
[srcA11y]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.a11y.css Accessibility specific styles, used across the framework and the [components](/components/start): - **`:focus-visible`**: a unified keyboard focus ring on everything. Customizable in your theme via the `--focus-ring-color`, `--focus-ring-width`, and `--focus-ring-offset` tokens. - **`.a11y-skipLink`**: skip-to-content link. It stays off-screen until focused. - **`.a11y-srOnly`**: hides the element visually, but keeps it accessible to screen readers. Place the skip link first in your ``, pointing at the id of your main content: ```html Skip to content
``` ## Animation
| File name | Source | | ---------------------- | ---------------------- | | `global.animation.css` | [Github][srcAnimation] |
[srcAnimation]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/global.animation.css Basic fade in/out animations with flexible start/end opacity values. Each keyframe only pins one end of the fade; the other end is the element's own `opacity`: | Keyframes | What it does | | --------------------- | ------------------------------------- | | `fadeFromTransparent` | fades from 0 to the element's opacity | | `fadeFromOpaque` | fades from 1 to the element's opacity | | `fadeToTransparent` | fades from the element's opacity to 0 | | `fadeToOpaque` | fades from the element's opacity to 1 | ```css .element { opacity: 0.8; animation: fadeToTransparent 1s forwards; } ``` (More info about [forwards](/blog/css-animation-fill-modes).) ## Media queries | File name | Source | | ---------------------------- | ------------- | | `settings.media-queries.css` | [Github][src] | [src]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/settings.media-queries.css All the breakpoints, user preferences, and device capabilities, as named [custom media queries][custom] you can use anywhere in your CSS. [Custom media queries][custom] are not yet available without a [PostCSS plugin][postcss]. If you can't or don't want to add a build step to your process, you'll have to hard-code your breakpoints. (You can still use nesting as in the example below though.) ### Viewport size #### Dimensions
| Token keyword | Dimension (px) | | ------------- | -------------- | | `xxs` | 0-240 | | `xs` | 240-360 | | `sm` | 360-480 | | `md` | 480-768 | | `lg` | 768-1024 | | `xl` | 1024-1440 | | `xxl` | 1440-1920 |
#### Variations Using the `md` dimension as an example:
| Token variation | Matches | | --------------- | ---------------------------- | | `--md-only` | exact range | | `--md-n-above` | range top and above | | `--md` | shorthand for `--md-n-above` | | `--md-n-below` | range top and below | | `--md-phone` | exact range in portrait only |
#### Examples
| Custom media query | Value | | ------------------------- | ------------------------------- | | `@media(--md-only) {}` | `(480px <= width < 768px);` | | `@media(--md) {}` | `(width >= 768px);` | | `@media(--md-n-above) {}` | `(width >= 768px);` | | `@media(--md-n-below) {}` | `(width < 768px);` | | `@media(--md-phone) {}` | `(--md-only) and (--portrait);` |
The `-phone` variant is available from `xxs` through `lg` (there is no `xl-phone` or `xxl-phone`). #### Recommended responsive setup If you search online for the best approach to responsive design and setting up your breakpoints, you'll come across the technically true but useless "it depends" answer. Unless you have a good reason not to, you should use a mobile-first approach. What that means is your design works great on small screens out of the box, without any media queries, and then you add your tweaks for larger sizes. This is how the CSS of most responsive components should be set up: ```css .exampleComponent { /* default mobile */ @media (--lg) { /* responsive tweaks for desktop */ } } ``` Without [PostCSS plugins][postcss]: ```css .exampleComponent { /* default mobile */ @media (width >= 1024px) { /* responsive tweaks for desktop */ } } ``` ### User preferences These map to the `prefers-*` and related media features, so you can respect user settings without memorizing the syntax:
| Custom media query | Matches | | ------------------ | ----------------------------------------------- | | `--motionOK` | `(prefers-reduced-motion: no-preference)` | | `--motionNotOK` | `(prefers-reduced-motion: reduce)` | | `--opacityOK` | `(prefers-reduced-transparency: no-preference)` | | `--opacityNotOK` | `(prefers-reduced-transparency: reduce)` | | `--useDataOK` | `(prefers-reduced-data: no-preference)` | | `--useDataNotOK` | `(prefers-reduced-data: reduce)` | | `--OSdark` | `(prefers-color-scheme: dark)` | | `--OSlight` | `(prefers-color-scheme: light)` | | `--highContrast` | `(prefers-contrast: more)` | | `--lowContrast` | `(prefers-contrast: less)` | | `--invertedColors` | `(inverted-colors: inverted)` | | `--forcedColors` | `(forced-colors: active)` |
### Device capabilities
| Custom media query | Matches | | ------------------ | -------------------------------------------- | | `--portrait` | `(orientation: portrait)` | | `--landscape` | `(orientation: landscape)` | | `--HDcolor` | `(dynamic-range: high) or (color-gamut: p3)` | | `--touch` | `(hover: none) and (pointer: coarse)` | | `--stylus` | `(hover: none) and (pointer: fine)` | | `--pointer` | `(hover) and (pointer: coarse)` | | `--mouse` | `(hover) and (pointer: fine)` |
#### Example These are used like any other custom media query, and they can be combined with each other or with a dimension token: ```css .exampleComponent { @media (--landscape) and (--touch) { /* a phone or tablet held sideways */ } } ``` Without [PostCSS plugins][postcss], the same thing spelled out: ```css .exampleComponent { @media (orientation: landscape) and ((hover: none) and (pointer: coarse)) { /* a phone or tablet held sideways */ } } ``` [custom]: https://drafts.csswg.org/mediaqueries-5/#custom-mq [postcss]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media --- # Getting Started > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/docs/start mCSS is both a CSS framework and a methodology. You need to first understand the methodology to use the framework correctly. There are 3 main parts to the methodology. 1. The file structure 1. The CSS syntax 1. The component architecture Once you've read through the basics below, have a look at the [source code][src] to see how it all comes together, and look at [Installation](/docs/installation) to choose the best option for your project. ## mCSS file structure
### Two folders **[`framework/`][framework-src]**: the core files you should not edit yourself: - Settings - Base - HTML elements - Global styles - Components (optional) - Themes (the default theme, plus optional swappable themes) - Helpers **[`site/`][site-src]**: your own CSS. These files do not use CSS [layers][layers] so **understanding and using [ITCSS][1] methodology there is key**. (The [mCSS own][site-src] `/site` folder is a good reference.) - Custom theme - Custom layouts - Your components - Page specific overrides - Patches to vendor plugins, packages, etc. ### The layers mCSS is a modern take on [ITCSS][1]. Files are organized in [layers][layers] going from broad and generic (settings → reset → HTML elements) to specific and local (components → helpers). This methodology addresses all the supposed "shortcomings" of CSS such as [specificity][3] wars and cascading conflicts. While ITCSS used specificity for its layers, mCSS upgrades that with native [CSS cascade layers][cascade-layers] priority. Every framework file is imported into a named `@layer`, and the layer order (not the import order, and not specificity) dictates the priority. By default, anything you write _outside_ the layers (in the `/site` folder) has higher priority. The only exception is [helpers][helpers] which use `!important` to override everything, by design. Here are all the mCSS layers, from lowest to highest priority:
| Layer | Files | What lives there | | --------------- | ----------------- | ----------------------------------------------- | | `base` | `base.*` | Reset/normalize. | | `elements` | `elements.*` | Default styling of bare HTML elements. | | `global` | `global.*` | Grid, wrap, layout scaffolds, prose, a11y. | | `components` | `component.*` | Self-contained component styles. | | `theme.default` | `theme.default.*` | Default [theme][themes] files. | | `theme.user` | `theme.*` | Custom theme override. | | `external` | _(your imports)_ | Code from plugins, npm packages, etc. | | `helpers` | `help.*` | Tailwind style utility classes, but done right. | | _(unlayered)_ | your own CSS | Beats every layer above, except helpers. |
Have a look at [`framework/mcss.css`][mcss-entry] to see how it's organized. All layers are _defined_ in this file. **Notes:** - `settings.*` files conceptually occupy the top position of the ITCSS "inverted pyramid" with lowest specificity, but since they're only used at build-time, they don't need their own `@layer`. - The `external` layer ships empty. This layer is reserved for third-party CSS (npm packages, plugins, etc.). That way you can use `@import url(…) layer(external);` to import vendor styles and it becomes trivial to override with your own CSS in a `patch.*` file (like this site's `patch.astro.css`). ### The global import The global import ([`_global.css`][4] for this site) is where it all comes together. You import everything used on your site there: - The mCSS framework - The mCSS components (optional) - The default theme - Your theme (optional override) - Your custom layouts, components, and page overrides - Vendor style patches ```css /* global.css */ @import url(./framework/mcss.css); /* Optional: the mCSS component library. */ @import url(./framework/mcss.components.css); /* The default theme. */ @import url(./framework/theme.default.css); /* Optional: your own theme, overriding the default */ @import url(./site/theme.starter.css); /* Your own CSS: it wins over every mCSS layer (except helpers) */ @import url(./site/global.layout.css); @import url(./site/component.header.css); @import url(./site/page.blog.css); /* etc. */ ``` [cascade-layers]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer [mcss-entry]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/mcss.css ## Detailed description of all files
### Settings Two `settings.*` files sit outside the `@layer` stack: they're only used at build time. - [Media queries][6] include responsive sizes, as well as user preferences like color schemes, reduced motion, etc. See [media queries docs][6]. - **Mixins** (optional). It is not used in other parts of mCSS by default but can be useful to streamline your own components' code. It requires a [PostCSS plugin][7] to work. ### Base - Simple reset/normalize. ### HTML Elements The default styling of all HTML elements, without classes. - **Sectioning:** `header`, `footer`, etc. - **Text:** `a`, `p`, etc. - **Quotes:** adds the correct quotes depending on language. - **Media:** `img`,`video`, etc. - **Table:** `table`, `th`, etc. - **Form:** `input`, `button`, etc. - **Interactive:** `dialog`, `details`. ### Global Global styles included out of the box: - A responsive [grid system][grid]. - A full feature [wrapper][wrap]. - Common global site [layouts][layout]. - [Typography][prose], via the `.prose` class, for long form text, like articles, etc. - [Accessibility][a11y] (A11Y) specific classes. - Basic `@keyframes` animations (e.g., fade in/out) ### Components Self-contained styles for single components. mCSS is designed to let you to create your own components, but a [collection of components][components] built on top of mCSS is included. It ships separately to keep mCSS nimble as the component library grows. **Note:** Some components are CSS-only. A single class on a single element, with no associated HTML. For example, the `.badge` class on a `` is the whole badge, and the `.bt` class can style a ``, code: '', }, }} controls={[ { heading: "Behavior", items: [ { type: "checkbox", name: "close", label: "Dismissible", snippet: "close", default: true }, ]}, { heading: "Content", items: [ { type: "text", name: "content", label: "Text", default: "mCSS v1 is out!" }, ]}, ]} /> The playground's dismiss button is inert: the persistence script lives in Banner.astro (see below). ## HTML ```html ``` For a dismissible plain-HTML banner, copy the small inline script from the bottom of [Banner.astro][2]: it removes the banner when the stored key exists (before paint, so an already-dismissed banner never flashes) and stores the key on click. Looking for the big closing call-to-action band instead? That's a [Section recipe](/components/section), not a separate component. ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
| Property | Description | | --------------------------- | ------------------- | | `--banner-color` | Banner text color. | | `--banner-background-color` | Banner background. |
## Astro component
| Prop | Type | Default | Description | | -------------- | --------- | ------------------------- | -------------------------------------------------------- | | `id` | `string` | `undefined` | Required when dismissible; keys the `localStorage` entry (`mcss-banner-`). Use a new id per campaign so a new announcement shows again. | | `dismissible` | `boolean` | `false` | Adds the close button + persistence script. | | `dismissLabel` | `string` | `"Dismiss announcement"` | Accessible name of the close button. | | `class` | `string` | `undefined` | Additional CSS classes. |
The default slot is the banner content. Place the banner as the first child of ``, above the header. ## Examples
  • A dismissible banner. Close it and it stays closed on reload (per browser).

    ```astro --- import Banner from "../components/Banner.astro"; ---

    mCSS v1 is out! Read the announcement

    ```
--- # Breadcrumbs > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/breadcrumbs The `.breadcrumbs` component is a `nav > ol` trail with CSS-only separators, and optional [BreadcrumbList JSON-LD](https://developers.google.com/search/docs/appearance/structured-data/breadcrumb) for search engines.
| File | Description | Source | | ---------------------------------------- | ------------------------------ | ----------- | | `component.breadcrumbs.css` | All breadcrumb styles | [Github][1] | | `Breadcrumbs.astro` | The Astro component | [Github][2] | | `--breadcrumbs-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][3] |
[1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.breadcrumbs.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/components/Breadcrumbs.astro [3]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground
  1. Home
  2. Components
  3. Breadcrumbs
`} baseClasses="breadcrumbs" controls={[ { heading: "Style", items: [ { type: "text", name: "sep", label: "Separator", default: "/" }, ]}, ]} /> ## HTML ```html ``` The last crumb is text, not a link, marked with `aria-current="page"`; the separators are generated content with empty alt text, so screen readers read the trail as a clean list. The separator lives in CSS, not the markup — change it by overriding one token: ```css :root { --breadcrumbs-separator: "→"; } ``` ### Custom properties
| Property | Description | | ------------------------------- | ---------------------------------- | | `--breadcrumbs-separator` | Separator string (default `/`). | | `--breadcrumbs-separator-color` | Separator color. | | `--breadcrumbs-color` | Link color. | | `--breadcrumbs-color-current` | Current page (last crumb) color. |
## Astro component
| Prop | Type | Default | Description | | ----------- | --------------------------- | --------------- | ------------------------------------------------------------------ | | `items` | `{ label, href? }[]` | — | The trail. The last item (or any item without `href`) is text. | | `ariaLabel` | `string` | `"Breadcrumbs"` | The nav's accessible name. | | `jsonLd` | `boolean` | `false` | Emit BreadcrumbList structured data (hrefs resolved to absolute). | | `class` | `string` | `undefined` | Additional CSS classes. |
## Examples
  • ```astro --- import Breadcrumbs from "../components/Breadcrumbs.astro"; --- ```
--- # Button > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/button The button is a [CSS-only component](/docs/start#components) using the `.bt` class (canonical; `.button` is kept as an alias). Apply it to `
| File | Description | Source | | ----------------- | ----------- | ----------- | | `component.button.css` | All button styles (`.button` or `.bt`) | [Github][1] | [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.button.css
## Playground {icon}{label}'} baseClasses="bt" snippets={{ icon: { preview: mail, code: "[…] " } }} controls={[ { heading: "Variation", items: [ { type: "select", name: "size", label: "Size", default: "", options: [ { label: "Default", value: "" }, { label: "Medium", value: "bt-md" }, { label: "Large", value: "bt-lg" }, ]}, { type: "select", name: "color", label: "Color", default: "", options: [ { label: "Default", value: "" }, { label: "Primary", value: "bt-primary" }, { label: "Yes", value: "bt-yes" }, { label: "Maybe", value: "bt-maybe" }, { label: "No", value: "bt-no" }, { label: "White", value: "bt-white", previewSurface: "dark" }, { label: "White outline", value: "bt-outline-white", previewSurface: "dark" }, ]}, ]}, { heading: "Modifiers", items: [ { type: "checkbox", name: "outline", label: "Outline", value: "bt-outline", default: false }, { type: "checkbox", name: "text", label: "Text button", value: "bt-text", default: false }, { type: "checkbox", name: "icon", label: "Icon", snippet: "icon", default: false }, { type: "checkbox", name: "tightIcon", label: "Tight icon", value: "bt-tightIcon", default: false }, ]}, { heading: "Content", items: [ { type: "text", name: "label", label: "Label", default: "Button" }, ]}, { heading: "State", items: [ { type: "checkbox", name: "disabled", label: "Disabled", attr: "disabled", default: false }, ]}, ]} /> ## HTML You can combine modifiers in any way you like (see [examples](#examples)). The `.bt-outline-white` and `.bt-white` (solid, primary text) are special case buttons useful for dark/image/gradient backgrounds such as [Heroes](/components/hero) and don't change between light/dark themes.
### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
| Property | Description | | ------------------------------------- | --------------------------------- | | `--bt-font-size` | Font size. | | `--bt-font-size-lg` | Large font size. | | `--bt-font-weight` | Font weight. | | `--bt-color` | Text color. | | `--bt-color-hover` | Text color (hover). | | `--bt-background-color` | Background color. | | `--bt-background-color-hover` | Background color (hover). | | `--bt-border-color` | Border color. | | `--bt-border-width` | Border width. | | `--bt-border-radius` | Border radius. | | `--bt-border-radius-lg` | Large border radius. | | `--bt-outline-color-hover` | Outline text color (hover). | | `--bt-outline-background-color-hover` | Outline background color (hover). | | `--bt-outline-border-color` | Outline border color. | | `--bt-primary-color` | Primary text color. | | `--bt-primary-color-hover` | Primary text color (hover). | | `--bt-primary-background-color` | Primary background color. | | `--bt-primary-background-color-hover` | Primary background color (hover). | | `--bt-primary-border-color` | Primary border color. | | `--bt-primary-border-color-hover` | Primary border color (hover). |
### Examples
  • This is a Link ```html This is a Link ```
  • ```html ```
  • ```html ```
--- # Card > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/card The `.card` component, in its simplest form, is a container with 3 style options: - **Default:** transparent background, border outline. - **Filled:** solid background color. - **Raised:** drop shadow and hover effect. Make sure you check out the [Tiles](/components/tiles) component to see how to lay cards out in equal-height, container-responsive columns. (See [below](/components/card#blog-index-pattern) and [this site's own blog index](/blog) for examples.)

Natural Law, is a set of universal, non-man-made, binding and unchangeable conditions which govern the consequence of our actions.

Natural Law, is a set of universal, non-man-made, binding and unchangeable conditions which govern the consequence of our actions.

| File | Description | Source | | ------------------------------------- | ---------------------------------------------- | ----------- | | `component.card.css` | All card styles (`.card`) | [Github][1] | | `Card.astro` | The Astro component | [Github][3] | | `Tags.astro`, `Avatar.astro` | Composed components (dependencies) | [Github][3] | | [Tiles](/components/tiles) | Equal-height, container-responsive list layout | [Github][2] | | `--card-*` block in `theme.default.ui.css` | UI tokens (see below) | [Github][4] |
[1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.card.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.tiles.css [3]: https://github.com/minimaldesign/mCSS/tree/main/src/components [4]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground {media}{tags}

{title}

{subtitle}

{badges}

{body}

{byline}{actions} `} baseClasses="card" snippets={{ mediaInset: { preview: `
`, code: `
`, }, mediaFlush: { preview: `
`, code: `
`, }, badges: { preview: '
new
', code: '
new
', }, tags: { preview: '', code: '', }, byline: { preview: '
SR
Sonny Rollins
', code: '
SR
Sonny Rollins
', }, actions: { preview: '', code: '', }, }} controls={[ { heading: "Variation", items: [ { type: "select", name: "variant", label: "Variant", default: "", options: [ { label: "Default", value: "" }, { label: "Filled", value: "card-filled" }, { label: "Raised", value: "card-raised" }, ], }, { type: "select", name: "media", label: "Image", default: "", snippet: "media", options: [ { label: "None", value: "" }, { label: "Inset", value: "inset", class: "", snippet: "mediaInset", }, { label: "Flush", value: "flush", class: "", snippet: "mediaFlush", }, ], }, ], }, { heading: "Parts", items: [ { type: "checkbox", name: "badges", label: "Badges", snippet: "badges", default: false, }, { type: "checkbox", name: "tags", label: "Tags", snippet: "tags", default: false, }, { type: "checkbox", name: "byline", label: "Byline", snippet: "byline", default: false, }, { type: "checkbox", name: "actions", label: "Actions", snippet: "actions", default: false, }, ], }, { heading: "State", items: [ { type: "checkbox", name: "disabled", label: "Disabled", value: "is-disabled", default: false, }, ], }, { heading: "Content", items: [ { type: "text", name: "title", label: "Title", default: "Card title" }, { type: "text", name: "subtitle", label: "Subtitle", default: "Category", }, { type: "text", name: "body", label: "Body", default: "This is where the content goes!", }, ], }, ]} /> ## HTML ### Available modifiers
| Available modifiers | Description | | ----------------------------------- | -------------------------------------------------- | | `.card` | Default style. Transparent background with border. | | `.card` + `.card-filled` | Filled with a background color. | | `.card` + `.card-raised` | Drop shadow, animated on hover. | | `.card` + `.is-disabled` | Lowered opacity. Interactivity disabled. | | `.card_media` + `.card_media-flush` | Edge-to-edge image on top of the card. |
### Clickable cards A card carries **one** link: put `.card_link` on a real anchor (usually the title link) and it stretches over the whole card with a `::after`, so screen readers hear one link named by the title. Actions stay clickable because `.card_actions` is positioned and comes later in the DOM; tags stay clickable through their own z-index bump. The blog-index pattern combines this with a flush image, tags, and a byline, on a [Tiles](/components/tiles) list. Tiles is container-responsive: columns appear as the column the list sits in crosses the size's container thresholds, so this site's blog index gets its 4-up laptop layout purely by widening the page column (`--layout-content-width`), no per-list tuning: ```html ``` ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
| Property | Color | | ----------------------------- | ----------------------------------------------- | | `--card-spacing` | General padding inside the card. | | `--card-color` | Text color. | | `--card-secondary-text-color` | Subtitle, date, and secondary text color. | | `--card-bg-color` | Background color of the "filled" variant. | | `--card-border-radius` | Border radius. | | `--card-border-color` | Border color. | | `--card-border-width` | Border width. | | `--card-shadow` | Default drop shadow or the "raised" variant. | | `--card-shadow-hover` | Hover state drop shadow. | | `--card-aspect-ratio` | Image aspect ratio (defaults to `--ar-golden`). | | `--card-loading-bg` | Loading skeleton background color. |
Badge colors come from the [badge component](/components/badge)'s `--badge-*` tokens. ## Astro component **Card** Every part is optional; render only what you pass. Badges and tags can coexist, but they compete for attention: badges are non-interactive status labels pinned top-right, tags are taxonomy links in the content flow. Prefer one or the other on a given card.
| Prop | Type | Default | Description | | -------------- | ------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------- | | `variant` | `string` | undefined | Card’s visual style: `filled`, `raised`. | | `isDisabled` | `boolean` | `false` | Disabled style. Disabled interactions. | | `isLoading` | `boolean` | `false` | Loading state with skeleton UI. | | `href` | `string` | undefined | Makes the whole card clickable (through the title link, if any). | | `title` | `string` | undefined | Header title text. | | `subtitle` | `string` | undefined | Header subtitle text. | | `headingLevel` | `number` | `2` | Heading level of the title: `2`, `3`, or `4`. | | `imageSrc` | `ImageMetadata \| string` | undefined | Add an image to the card (imported asset or URL/path). | | `imageAlt` | `string` | `''` | Alt text for the image. | | `aspectRatio` | `string` | undefined | Image aspect ratio (available [aspect ratios][ar]); unset, the `--card-aspect-ratio` token applies (golden ratio). | | `flushImage` | `boolean` | `false` | Edge-to-edge image on top of the card instead of inset. | | `badges` | `array` | `[]` | Array of objects (see example below). | | `tags` | `string[]` | `[]` | Tag names, rendered with the Tags component. | | `tagsUrl` | `string` | `'/blog/tags'` | Base URL for tag links. | | `author` | `string` | undefined | Byline name (also feeds the Avatar initials). | | `avatarSrc` | `ImageMetadata \| string` | undefined | Byline avatar image. | | `date` | `Date` | undefined | Publication date, rendered as a `
**Tiles** Lists of cards go in a [Tiles](/components/tiles) component: a `ul` whose items stretch, so cards in a row share the same height, with a container-responsive column count. See its page for the sizes, thresholds, and container rules. ### Slots The `Card` component supports the following slots for content projection: - **Default slot**: Main content area (e.g., text or paragraphs). - **Actions slot**: Action buttons or links in the footer. It is recommended to use the [button component](/components/button) in the `actions` slot, e.g.: ```astro Cancel ``` ### Badge Styling The `badges` prop takes an array of objects. Each object consists of a `label` property which defines the badge's text, and an optional `color` property that defines the badge's background color. If no `color` is provided, it defaults to `--badge-background-color` from the [badge component](/components/badge).

This is where the content goes. Unless you only want a bunch of badges!

```astro

This is where the content goes. Unless you only want a bunch of badges!

``` ## Examples
  • ### Blog-index pattern
  • Why layer order beats specificity wars, and how mCSS hands the final word to your unlayered CSS.

  • One file of custom properties is the whole theming story.

  • ```astro --- import Card from '../components/Card.astro'; import Tiles from '../components/Tiles.astro'; ---
  • Why layer order beats specificity wars.

  • ```
  • ### Card with image and actions

    This is a extremely detailed description of the product.

    View Details
    ```astro --- import placeholder from "../../assets/images/image.png"; import Card from '../../components/Card.astro'; ---

    This is a extremely detailed description of the product.

    View Details
    ```
  • ### Loading state

    This is what the card looks like without the `isLoading` prop.

    This is what the card looks like without the `isLoading` prop.

    ```astro --- import placeholder from "../../assets/images/image.png"; import Card from '../../components/Card.astro'; ---

    This is what the card looks like without the `isLoading` prop.

    ```
  • ### Disabled state

    With the `isDisabled` prop, the link doesn't work anymore.

    Witout the `isDisabled` prop, the link is clickable.

    ```astro --- import placeholder from "../../assets/images/image.png"; import Card from '../../components/Card.astro'; ---

    With the `isDisabled` prop, the link doesn't work anymore.

    ```
  • ### HTML example

    This is a extremely detailed description of the product.

    View Details
    ```html ```
--- # FAQ > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/faq The `.faq` component groups `details`/`summary` disclosures into the classic FAQ accordion — native behavior, zero JavaScript.

No. The browser's own details element does all the work.

Every item in this group shares name="demo", so the browser only keeps one open. Remove the name and items open independently.

Natively: the summary is focusable and toggles with Enter/Space.

| File | Description | Source | | -------------------------------- | --------------------------------------- | ----------- | | `component.faq.css` | Group spacing | [Github][1] | | `elements.interactive.css` | The `details`/`summary` styling + chevron | [Github][2] | | `Faq.astro`, `FaqItem.astro` | The Astro components | [Github][3] | | `--faq-spacing` in `theme.default.ui.css` | UI token | [Github][4] |
[1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.faq.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.interactive.css [3]: https://github.com/minimaldesign/mCSS/tree/main/src/components [4]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground
Does this need JavaScript?

No. The browser's own details element does all the work.

How does exclusive-open work?

Every item shares the same name attribute.

`} snippets={{ group: { preview: ' name="faq"', code: ' name="faq"' }, open: { preview: " open", code: " open" }, }} controls={[ { heading: "Behavior", items: [ { type: "checkbox", name: "group", label: "Exclusive open", snippet: "group", default: true }, { type: "checkbox", name: "open", label: "First item open", snippet: "open", default: false }, ]}, ]} /> ## HTML ```html
Does this need JavaScript?

No. The browser's own details element does all the work.

How does exclusive-open work?

Every item shares the same name attribute.

``` Giving every item the same `name` makes the group exclusive-open (opening one closes the others). It is a progressive enhancement: browsers that don't support exclusive accordions simply let several items stay open, which is a perfectly fine FAQ too. ### Custom properties
| Property | Description | | --------------- | ------------------- | | `--faq-spacing` | Gap between items. |
The item visuals come from the `details` element styling; there is nothing FAQ-specific to retheme. ## Astro component **Faq** takes only `class` and passes everything else to its root `
`. Items go in the default slot. **FaqItem**
| Prop | Type | Default | Description | | ---------- | --------- | ----------- | ---------------------------------------------------------------- | | `question` | `string` | — | The summary text. Required. | | `open` | `boolean` | `false` | Render the item open. | | `name` | `string` | `undefined` | Same name on every item of a group = native exclusive-open. | | `class` | `string` | `undefined` | Additional CSS classes. |
The default slot is the answer. ## Examples
  • No `name`, so this group lets several items stay open at once.

    The first item above renders open.

    ```astro --- import Faq from "../components/Faq.astro"; import FaqItem from "../components/FaqItem.astro"; ---

    No `name`, so several items can stay open at once.

    The first item above renders open.

    ```
--- # Feature Grid > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/featuregrid export const check = ``; The feature grid is the classic marketing "three columns of icon + title + text" pattern: `.featureItem` blurbs on a small-size [Tiles](/components/tiles) list. The layout is container-responsive: columns appear as the nearest size container gets wider, so the count answers the space the grid actually has, not the viewport.

Just an unordered list and the tiles you already have.

1, 2, then n columns as its container grows.

Icon box size and colors come from the theme.

| File | Description | Source | | -------------------------------------------- | ---------------------------------- | ----------- | | `component.featureItem.css` | Feature item styles | [Github][1] | | `FeatureItem.astro` | The Astro component | [Github][2] | | `--featureItem-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][3] | | [Tiles](/components/tiles) | The container-responsive layout | [Github][2] |
Depends on [Tiles](/components/tiles) for the layout. Icons are yours to bring; [Lucide](https://lucide.dev/) is a great resource. [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.featureItem.css [2]: https://github.com/minimaldesign/mCSS/tree/main/src/components [3]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground
  • Zero JavaScript

    Just an unordered list and the tiles you already have.

  • Responsive by default

    1, 2, then n columns as its container grows.

  • Tokenized icons

    Icon box size and colors come from the theme.

  • `} baseClasses="tiles" snippets={{ check: { preview: check, code: "[…]" }, }} controls={[ { heading: "Layout", items: [ { type: "select", name: "size", label: "Tile size", default: "sm", options: [ { label: "sm (the pattern)", value: "sm", class: "tiles-sm" }, { label: "md", value: "md", class: "" }, ], }, ], }, ]} /> ## HTML One `.featureItem` per `
  • `, on a `.tiles-sm` list: small tiles, so its container thresholds sit lower and columns appear sooner than for card-sized ones. The count comes from the width of the column the list sits in; see [Tiles](/components/tiles) for the thresholds and the container rules. ```html
    • Zero JavaScript

      Just an unordered list and the tiles you already have.

    ``` The icon box is `aria-hidden`: feature icons are decoration next to a real heading, so screen readers skip them. ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
    | Property | Description | | ------------------------------------- | ---------------------------- | | `--featureItem-icon-box-size` | Icon container width/height. | | `--featureItem-icon-size` | SVG width/height. | | `--featureItem-icon-color` | Icon (stroke/fill) color. | | `--featureItem-icon-background-color` | Icon container background. | | `--featureItem-icon-border-radius` | Icon container radius. |
    ## Astro component The list is the [Tiles](/components/tiles) component with `size="sm"`. **FeatureItem**
    | Prop | Type | Default | Description | | ------------ | -------- | ----------- | -------------------------------------------- | | `title` | `string` | `undefined` | The item's `h3`. | | `class` | `string` | `undefined` | Additional CSS classes. | | `titleClass` | `string` | `undefined` | Extra classes added to `.featureItem_title`. | | `iconClass` | `string` | `undefined` | Extra classes added to `.featureItem_icon`. |
    | Slot | Description | | --------- | ------------------------------------------------ | | `icon` | An SVG icon, rendered in the tokenized icon box. | | (default) | The item's body text. |
    ## Examples
    • Pin the count with `--tiles-columns` on a class of yours when the thresholds shouldn't decide.

      Skip the `icon` slot and the box disappears.

      ```astro --- import Tiles from "../components/Tiles.astro"; import FeatureItem from "../components/FeatureItem.astro"; import zap from "../assets/icons/zap.svg?raw"; ---

      Pin the count with `--tiles-columns` on a class of yours when the thresholds shouldn't decide.

      Skip the `icon` slot and the box disappears.

      ``` ```css .perks { --tiles-columns: 2; } ```
    --- # Form Field > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/field The `.field` component composes the bare [form elements](/docs/default-html) into label + control + hint + error message, with validation states driven entirely by CSS.
    Type something that isn't an email address and tab away: the hint swaps for the error and the border turns red, no JavaScript involved.
    | File | Description | Source | | ----------------------------------- | ---------------------------------------- | ----------- | | `component.field.css` | Field structure and states | [Github][1] | | `component.fieldRow.css` | fieldRow composition (own block) | [Github][5] | | `elements.form.css` | Bare element styles + `:user-invalid` border | [Github][2] | | `Field.astro` | The Astro component | [Github][3] | | `--field-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][4] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.field.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.form.css [3]: https://github.com/minimaldesign/mCSS/blob/main/src/components/Field.astro [4]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css [5]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.fieldRow.css ## Playground {hint} {error}
  • `} baseClasses="field" controls={[ { heading: "State", items: [ { type: "checkbox", name: "required", label: "Required", attr: "required", default: true }, { type: "checkbox", name: "disabled", label: "Disabled", attr: "disabled", default: false }, { type: "checkbox", name: "invalid", label: "Invalid", value: "is-invalid", default: false }, ]}, { heading: "Content", items: [ { type: "text", name: "label", label: "Label", default: "Email" }, { type: "text", name: "hint", label: "Hint", default: "We'll never share it.", markup: '{value}', attr: 'aria-describedby="pg-email-hint"' }, { type: "text", name: "error", label: "Error", default: "That doesn't look like an email address." }, ]}, ]} /> ## HTML ```html
    We'll never share it. That doesn't look like an email address.
    ``` Errors use `:user-invalid`, so a pristine form never lights up red: only fields the user actually touched and got wrong. The error message is always in the markup and revealed by `.field:has(:user-invalid)`. Add `.is-invalid` on the `.field` wrapper to force the same presentation from markup, for server-side validation results, tests, and demos (that's what the playground's Invalid checkbox does). It is deliberately **not** referenced by `aria-describedby`: screen readers get the browser's native validation message on submit, so wiring the visual error in as a description would double-announce it. ### Custom properties
    | Property | Description | | ------------------------------ | ------------------------------------------ | | `--field-spacing` | Gap between label, control, and messages. | | `--field-hint-color` | Hint text color. | | `--field-error-color` | Error text color. | | `--field-message-position` | `static` (default) keeps messages in flow under the control; `absolute` overlays them below the field so they can't move siblings. `.fieldRow` sets `absolute`. | | `--input-border-color-invalid` | Border color of a `:user-invalid` control. |
    ## Astro component
    | Prop | Type | Default | Description | | ---------- | --------- | ---------------------- | ------------------------------------------------------------------ | | `label` | `string` | — | The visible label. Required. | | `hint` | `string` | `undefined` | Help text under the control. | | `error` | `string` | `"Please fill out…"` | Message revealed when the control is `:user-invalid`. | | `class` | `string` | `undefined` | Additional CSS classes on the `.field` wrapper. |
    Everything else (`type`, `name`, `placeholder`, `required`, `pattern`, `minlength`…) is passed through to the ``. The label's `for` uses `id`, falling back to `name` — set at least one. To use another control (textarea, select…), put it in the default slot and give it the matching `id`: ```astro ``` ## Recipes ### Newsletter signup `.fieldRow` lines fields and buttons up on one wrapping row. Mix `.fieldRow_field` onto each field that should stretch. The row sets `--field-message-position: absolute`, so hints and errors overlay below the row instead of pushing the button out of line with the input (try it: type a bad address and tab away). Leave a little vertical room under the row for them:
    ```html
    ``` ### Contact form Fieldset + the [grid system](/docs/layout#grid) for the two-column row:
    Contact us
    ```astro --- import Field from "../components/Field.astro"; ---
    Contact us
    ``` --- # Footer > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/footer The `.footer` component is the site-wide bottom section: link columns on the right, a meta area (copyright, credits, version…) on the left. Pure HTML and CSS, no JavaScript.
    | File | Description | Source | | ----------------------------------- | ------------------------------ | ----------- | | `component.footer.css` | All footer styles | [Github][1] | | `Footer.astro` | The Astro component | [Github][2] | | `--footer-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][3] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.footer.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/components/Footer.astro [3]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## HTML The footer expects two regions: a `nav` with one `
      ` whose `
    • ` children are the columns (a heading plus a link list each), and a `.footer_meta` section. Pick a real heading level for the column titles: the next one in your page's outline (usually `h2`), sized down with a [heading helper class](/docs/helpers) like `.h5`. Jumping straight to an `h5` for its looks would leave a heading-level gap for screen-reader users on every page. ```html ``` ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
      | Property | Description | | --------------------------- | ----------------------------------------------- | | `--footer-background-color` | Background color. | | `--footer-text-color` | Text color. | | `--footer-border-width` | Top border width. | | `--footer-border-color` | Top border color (transparent in light theme). | | `--footer-link-color` | Link color. | | `--footer-link-color-hover` | Link hover color. |
      ## Astro component
      | Prop | Type | Default | Description | | ---------- | -------- | ----------- | ------------------------------------------------------------------- | | `navLabel` | `string` | `"Footer"` | `aria-label` for the footer nav. | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). |
      Any other attribute is passed through to the root `
      ` element.
      | Slot | Description | | --------- | --------------------------------------------------------------------------- | | (default) | The link columns: one `
        ` with one `
      • ` per column (heading + `
          `). | | `meta` | The meta area: copyright, credits, socials… |
      ## Examples
      • ```astro --- import Footer from "../components/Footer.astro"; --- ```
      • This site's own footer is the same component; see [SiteFooter.astro](https://github.com/minimaldesign/mCSS/blob/main/src/components/SiteFooter.astro) for a full example with social icons and a version line in the `meta` slot.
      --- # Header > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/header The `.header` component is the site-wide top bar: logo, primary navigation, optional actions, and a built-in mobile menu behind a hamburger button.
      Sign up
      | File | Description | Source | | ----------------------------------- | -------------------------------- | ----------- | | `component.header.css` | All header styles | [Github][1] | | `Header.astro` | The Astro component | [Github][2] | | `icons/menu.svg`, `icons/x.svg` | Mobile menu open/close icons | [Github][3] | | `--header-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][4] | | `scripts/utilities.js` (`throttle`) | Used by the sticky-shadow script | [Github][5] |
      [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.header.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/components/Header.astro [3]: https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons [4]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css [5]: https://github.com/minimaldesign/mCSS/blob/main/src/scripts/utilities.js ## HTML The header is a flex row: logo first, desktop nav pushed to the right, actions last. Below the `--md` breakpoint the desktop nav and actions hide and the hamburger appears. ```html
      ``` When using plain HTML you also need the small script that wires the hamburger; copy it from the bottom of [Header.astro][2]. It keeps the menu keyboard operable: `aria-expanded` reflects the state, focus moves into the menu on open and back to the trigger on close, and `Escape` closes it. ### Available modifiers
      | Class / state | Description | | ---------------------------- | -------------------------------------------------------------------------- | | `.header` | Base block. Flex row, `--header-height` tall. | | `.header` + `.header-sticky` | Sticks to the top of the viewport with a shadow once the page is scrolled. | | `.is-top` | State set by the script while the page is scrolled to the very top (hides the sticky shadow). | | `a[aria-current]` | Current page (`page`) or section (`true`) links are highlighted. |
      ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
      | Property | Description | | -------------------------------- | -------------------------------------------------- | | `--header-height` | Header height (grows at the `--md` breakpoint). | | `--header-padding` | Horizontal padding. | | `--header-background-color` | Background color. | | `--header-menu-background-color` | Mobile menu background (always dark by design). | | `--header-menu-color` | Mobile menu link color. | | `--header-menu-color-current` | Mobile menu current-page link color. | | `--header-menu-secondary-color` | Mobile menu sub-item link color. | | `--header-menu-border-color` | Mobile menu sub-list border. |
      The mobile menu deliberately keeps the same dark surface in both themes; retheme it through the `--header-menu-*` tokens rather than a `light-dark()` override. ## Astro component
      | Prop | Type | Default | Description | | ------------- | ----------- | ------------------- | ----------------------------------------------------------------- | | `navItems` | `NavItem[]` | `[]` | `{ label, href, children?, mobileOnly? }`. `children` renders as a sub-list in the mobile menu only; `mobileOnly` items are skipped in the desktop nav. | | `sticky` | `boolean` | `false` | Sticky positioning + scroll shadow. | | `currentPath` | `string` | `Astro.url.pathname`| Used to set `aria-current`: exact match gets `page`; the item whose `children` contain the page gets `true` (falling back to first-path-segment matching when none does). | | `navLabel` | `string` | `"Main"` | `aria-label` for the desktop nav. | | `menuLabel` | `string` | `"Main menu"` | `aria-label` for the mobile nav. | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). |
      Any other attribute is passed through to the root `
      ` element.
      | Slot | Description | | --------- | ------------------------------------------------------------------ | | `logo` | Brand link. Use `class="header_logo"` on it to pick up the styles. | | `actions` | Right-hand side extras (theme toggle, CTA). Desktop only. | | `menu` | Extra content at the top of the mobile menu (e.g. a theme toggle). |
      ## Examples
      • ```astro --- import Header from "../components/Header.astro"; ---
        ```
      • This site's own header is the same component; see [SiteHeader.astro](https://github.com/minimaldesign/mCSS/blob/main/src/components/SiteHeader.astro) for a full example with nested mobile navigation, `mobileOnly` sections, and theme toggles in the `actions` and `menu` slots.
      --- # Hero > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/hero The `Hero` component offers highly flexible, accessible hero sections for landing pages, product sites, and marketing. It supports full, split, and slideshow layouts; accepts image, video, gradient, or canvas backgrounds; provides overlay and text color controls; allows custom aspect ratios and content/media ordering; supports parallax, rounded corners, and a flush layout for the split variant; and is fully slot-based for custom content. ([Full size demo](/demos/hero).)

      Example Hero

      This hero uses an animated canvas element as background.

      | File | Description | Source | | -------------------- | ----------- | ----------- | | `component.hero.css` | All hero styles (`.hero`) | [Github][1] |
      [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.hero.css #### Full layout (default) The full layout spans the entire viewport with content centered over a background image, video, gradient, or canvas element. Use the `height` prop to control min-height (in `svh` units). #### Split layout The Split layout features an asymmetric split (e.g., `70/30`, `80/20`, etc.) with content on one side and media on the other. The media section fills the available space with an image or video, cropping as needed. The `ratio` prop defines the grid layout, `contentFirst` controls the DOM order of content and media, and `reversed` visually reverses their order (while the grid ratio remains unchanged). #### Slideshow layout The Slideshow layout features rotating content and background (same options available as for the Full layout) with pagination dots. Each slide can have its own media and content. Each `Hero` instance generates its own unique `id`, so multiple heroes on one page (including JavaScript-driven ones like slideshows) never cross-wire. Pass an explicit `id` prop only if you need a stable anchor to link to. ## HTML ### Elements
      | Class | Description | | --------------- | ------------------------------------------------------------------------------------------------- | | `.hero_actions` | Wrapping button row inside the hero content, centered on the full and slideshow variants (the hero twin of `.section_actions`). |
      ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
      | Property | Description | | ------------------------------------ | ----------------------------------------------- | | `--hero-radius` | Border radius (on/off via `roundCorners` prop). | | `--hero-content-padding` | Padding for hero content area. | | `--hero-actions-spacing` | Space above the `.hero_actions` button row. | | `--hero-pagination-dot` | Pagination dot size. | | `--hero-pagination-dot-color` | Pagination dot background color. | | `--hero-pagination-dot-active-color` | Active pagination dot color. | | `--hero-pagination-opacity` | Pagination dot default opacity. |
      ## Astro component ### General props
      | Prop | Type | Default | Description | | -------------- | ------- | ------- | ------------------------------------------------------------- | | `variant` | string | `full` | Layout variant: Full, Split, and Slideshow. | | `media` | object | — | Image, video, gradient, or canvas background. | | `roundCorners` | boolean | `false` | Whether the hero media has rounded corners. | | `textColor` | string | — | Text color (CSS variable or value). | | `class` | string | — | Additional CSS classes (see [helper classes](/docs/helpers)). |
      ### Split variant props
      | Prop | Type | Default | Description | | -------------- | ------- | ------- | ---------------------------------------------------------------------------------- | | `ratio` | string | `62/38` | Split ratio, e.g. `70/30`, `40/60`, etc. | | `contentFirst` | boolean | `false` | If true, content appears before media in the DOM (left on desktop, top on mobile). | | `reversed` | boolean | `false` | Visually reverses the order set by `contentFirst` (does not change DOM order). | | `roundCorners` | boolean | `false` | If true, media has rounded corners. | | `flush` | boolean | `false` | Removes outer padding from the content section, making text flush with the edge. |
      ### Full and slideshow variant props
      | Prop | Type | Default | Description | | --------------------- | ------- | ------------ | ------------------------------------ | | `parallax` | boolean | `false` | Parallax effect (full variant only). | | `height` | number | — | Sets `min-height` in `svh` (0-100). | | `overlayOpacity` | number | `0` | Overlay opacity. | | `overlayColor` | string | `--base-950` | Overlay color (CSS var or value). | | `roundCorners` | boolean | `false` | If true, hero has rounded corners. | | `textColor` | string | — | Text color (CSS var or value). | | `slideshowItems` | array | — | Array of objects (see example). | | `slideshowAutoplay` | boolean | `true` | Slideshow autoplay. | | `slideshowInterval` | number | `5000` | Slideshow interval (ms). | | `slideshowTransition` | number | `600` | Slideshow transition speed (ms). |
      ## Examples
      • ### Animated Canvas

        Animated Canvas Hero

        This hero uses an animated canvas element as background.

        Full Size Examples
        ```astro --- import Hero from "../../components/Hero.astro"; ---

        Animated Canvas Hero

        Organic Delaunay triangles animated with primary colors.

        Full Size Demo
        ```
      • ### Gradient background

        Gradient Background Hero

        Showcase your brand with a beautiful, vibrant gradient background.

        Full Size Examples
        ```astro --- import Hero from "../../components/Hero.astro"; ---

        Gradient Background Hero

        Showcase your brand with a beautiful, vibrant gradient background.

        Full Size Examples
        ```
      • ### Background image

        Image Background Hero

        This hero uses a static image as the background.

        Full Size Examples
        ```astro --- import Hero from "../../components/Hero.astro"; import triangle01 from "../../assets/images/triangles.svg"; ---

        Image Background Hero

        This hero uses a static image as the background.

        Full Size Examples
        ```
      • ### Slideshow
        • First Slide

          This is the first slide of the slideshow.

        • Second Slide

          This is the second slide of the slideshow.

        • Third Slide

          This is the third slide of the slideshow.

        ```astro --- import Hero from "../../components/Hero.astro"; import triangle01 from "../../assets/images/triangle-01.svg"; import triangle02 from "../../assets/images/triangle-02.svg"; import triangle03 from "../../assets/images/triangle-03.svg"; ---
        • First Slide

          This is the first slide of the slideshow.

        • Second Slide

          This is the second slide of the slideshow.

        • Third Slide

          This is the third slide of the slideshow.

        ```
      • ### Default split

        Split Hero (Content Right)

        Default split with content on the right and media on the left.

        ```astro --- import Hero from "../../components/Hero.astro"; import triangle01 from "../../assets/images/triangles.svg"; ---

        Split Hero (Content Right)

        Default split with content on the right and media on the left.

        ```
      • ### Content first split

        Split Hero (Content First)

        This example uses the contentFirst prop to place content before media in the DOM (left on desktop, top on mobile).

        ```astro --- import Hero from "../../components/Hero.astro"; import triangle01 from "../../assets/images/triangles.svg"; ---

        Split Hero (Content First)

        This example uses the contentFirst prop to place content before media in the DOM (left on desktop, top on mobile).

        ```
      • ### Content first reversed split

        Split Hero (Reversed)

        This example uses the reversed prop to visually flip the order of content and media, without changing their DOM order. The flush prop removes left padding.

        ```astro --- import Hero from "../../components/Hero.astro"; import triangle01 from "../../assets/images/triangles.svg"; ---

        Split Hero (Reversed)

        This example uses the reversed prop to visually flip the order of content and media, without changing their DOM order. The flush prop removes left padding.

        ```
      --- # Notice > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/notice The `.notice` component is useful to bring something to the attention of the reader/user, as well as provide feedback on user interaction.

      This is the the default notice style, without a modifier class.

      | File | Description | Source | | ---------------------- | ----------- | ----------- | | `component.notice.css` | All notice styles (`.notice`) | [Github][1] |
      [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.notice.css ## Playground
      {icon} {title}

      {body}

      `} baseClasses="notice" snippets={{ info: { preview: infoIcon, code: "[…]" }, danger: { preview: dangerIcon, code: "[…]" }, warning: { preview: warningIcon, code: "[…]" }, confirm: { preview: confirmIcon, code: "[…]" }, }} controls={[ { heading: "Variation", items: [ { type: "select", name: "type", label: "Type", snippet: "icon", default: "", options: [ { label: "Default", value: "", snippet: "info" }, { label: "Info", value: "notice-info", snippet: "info" }, { label: "Danger", value: "notice-danger", snippet: "danger" }, { label: "Warning", value: "notice-warning", snippet: "warning" }, { label: "Confirm", value: "notice-confirm", snippet: "confirm" }, ]}, ]}, { heading: "Content", items: [ { type: "text", name: "title", label: "Title", default: "Pro tip!" }, { type: "text", name: "body", label: "Body", default: "Notices work without JavaScript." }, ]}, ]} /> ## HTML ### Available modifiers
      | Available modifiers | Description | | ----------------------------- | ----------------------------- | | `.notice` | Default style. Base colors. | | `.notice` + `.notice-info` | Information. Primary colors. | | `.notice` + `.notice-danger` | Error/Danger. Reds. | | `.notice` + `.notice-warning` | Warning. Oranges. | | `.notice` + `.notice-confirm` | Confirmation/Success. Greens. |
      When using HTML, you'll need to include the SVG icons "manually." All the icons are [available on Github](https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons). If you'd like to use different icons, [Lucide](https://lucide.dev/) is a great resource. ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
      | Property | Color | | --------------------------- | ------------------------- | | `--notice-spacing` | Padding inside Notice. | | `--notice-text-color` | Default text color. | | `--notice-background-color` | Default background color. | | `--notice-border-color` | Default border color. | | `--notice-border-width` | Default border width. | | `--notice-border-radius` | Default border radius. |
      ## Astro component
      | Prop | Type | Default | Description | | ------------ | -------- | ----------- | ----------------------------------------------------------- | | `title` | `string` | `undefined` | Text for the notice header. (Optional.) | | `type` | `string` | `undefined` | `info`, `danger`, `warning`, `confirm`. | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). |
      ## Examples
      • This is a warning notice without a `title` prop.

        ```astro --- import Notice from "../../components/Notice.astro"; ---

        This is a warning notice without a `title` prop.

        ```
      • You've read this notice until the end!

        ```astro --- import Notice from "../../components/Notice.astro"; ---

        You've read this notice until the end!

        ```
      ### HTML examples
      • This is the the default notice style, without a modifier class.

        ```html ```
      --- # Pagination > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/pagination Two navigation patterns for content sites: `.pagination` is the numbered kind for blog indexes, `.prevNext` the pair of cards at the bottom of a post. Both are pure HTML and CSS. `#page-${p}`} />
      | File | Description | Source | | --------------------------------------------- | ------------------------------ | ----------- | | `component.pagination.css` | Numbered pagination styles | [Github][1] | | `component.prevNext.css` | Prev/next styles | [Github][2] | | `Pagination.astro`, `PrevNext.astro` | The Astro components | [Github][3] | | `icons/chevron-down.svg` | Arrow icon (rotated in CSS) | [Github][4] | | `--pagination-*`, `--prevNext-*` token blocks | UI tokens (see table below) | [Github][5] |
      [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.pagination.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.prevNext.css [3]: https://github.com/minimaldesign/mCSS/tree/main/src/components [4]: https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons [5]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground The numbered pagination's markup is generated by windowing logic (see [HTML](#html)), so the playground covers the `.prevNext` pattern: toggle either link off and the layout holds its columns. {prev}{next} `} snippets={{ prev: { preview: 'PreviousDesigning with cascade layers', code: 'PreviousDesigning with cascade layers', }, next: { preview: 'NextTokens all the way down', code: 'NextTokens all the way down', }, }} controls={[ { heading: "Parts", items: [ { type: "checkbox", name: "prev", label: "Previous link", snippet: "prev", default: true }, { type: "checkbox", name: "next", label: "Next link", snippet: "next", default: true }, ]}, ]} /> ## HTML The numbers are windowed around the current page, so long lists stay short. ```html ``` At the ends, the arrows stay in place but become disabled `` elements instead of links, so the layout never jumps. Every target is at least `--pagination-size` (36px) square. ```html ``` A lone next link keeps its right-hand column. ### Custom properties
      | Property | Description | | -------------------------------------- | --------------------------------- | | `--pagination-size` | Minimum width/height of a target. | | `--pagination-border-radius` | Target border radius. | | `--pagination-color` | Number/arrow color. | | `--pagination-color-current` | Current page text color. | | `--pagination-background-color-current`| Current page background. | | `--pagination-background-color-hover` | Hover background. | | `--prevNext-border-color` | Card border. | | `--prevNext-border-color-hover` | Card border on hover. | | `--prevNext-label-color` | "Previous"/"Next" label color. |
      ## Astro component **Pagination**
      | Prop | Type | Default | Description | | ------------- | -------------------------- | ---------------- | --------------------------------------------------------- | | `currentPage` | `number` | — | The page being rendered. Required. | | `lastPage` | `number` | — | Total number of pages. Required. | | `baseUrl` | `string` | — | Index URL; page 1 is `baseUrl`, page n is `baseUrl/n`. | | `pageUrl` | `(page: number) => string` | see `baseUrl` | Override the URL scheme entirely. | | `windowSize` | `number` | `1` | Pages shown on each side of the current one. | | `ariaLabel` | `string` | `"Pagination"` | The nav's accessible name. |
      With Astro's `paginate()`, pass `page.currentPage`, `page.lastPage`, and your index route as `baseUrl`. **PrevNext**
      | Prop | Type | Default | Description | | ----------- | -------------------------- | --------------------- | ---------------------------------- | | `prev` | `{ title, href }` | `undefined` | Previous post link. | | `next` | `{ title, href }` | `undefined` | Next post link. | | `prevLabel` | `string` | `"Previous"` | Small label above the prev title. | | `nextLabel` | `string` | `"Next"` | Small label above the next title. | | `ariaLabel` | `string` | `"Previous and next"` | The nav's accessible name. |
      Renders nothing when both links are missing. ## Examples
      • `#page-${p}`} /> ```astro --- import Pagination from "../components/Pagination.astro"; const { page } = Astro.props; // from paginate() --- ```
      • ```astro ```
      --- # Pricing > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/pricing The `.pricingCard` component is one tier of a pricing table: name, price, feature list, and a CTA pinned to the bottom so a row of tiers lines up. A pricing table is a row of these on the [grid](/docs/layout#grid), with `highlighted` marking the recommended tier.
      | File | Description | Source | | ------------------------------------------ | ----------------------------------- | ----------- | | `component.pricing.css` | Tier layout + highlight styles | [Github][1] | | `component.card.css`, `component.badge.css`, `component.button.css` | Base visuals (dependencies) | [Github][2] | | `PricingCard.astro` | The Astro component | [Github][3] | | `icons/check.svg` | Feature list check icon | [Github][4] | | `--pricingCard-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][5] |
      It extends the [card](/components/card) and uses the [badge](/components/badge) and [button](/components/button) components. [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.pricing.css [2]: https://github.com/minimaldesign/mCSS/tree/main/src/styles/framework [3]: https://github.com/minimaldesign/mCSS/blob/main/src/components/PricingCard.astro [4]: https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons [5]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground

      {name}

      {badge}

      {price} /month

      {description}

      • 10 sites
      • Email support
      `} baseClasses="card pricingCard" snippets={{ check: { preview: checkIcon, code: "[…]" }, badge: { preview: 'Popular', code: 'Popular' }, }} controls={[ { heading: "Modifiers", items: [ { type: "checkbox", name: "highlighted", label: "Highlighted", value: "pricingCard-highlighted", default: false }, ]}, { heading: "Content", items: [ { type: "checkbox", name: "badge", label: "Badge", snippet: "badge", default: false }, { type: "text", name: "name", label: "Name", default: "Pro" }, { type: "text", name: "price", label: "Price", default: "$29" }, { type: "text", name: "description", label: "Description", default: "For professionals." }, ]}, ]} /> ## HTML ```html

      Pro

      Popular

      $29 /month

      For professionals.

      • 10 sites
      • Email support
      ``` The highlight is a border-color swap plus a same-color ring (`box-shadow`), so the highlighted tier doesn't shift the row's layout. ### Custom properties
      | Property | Description | | ------------------------------------- | ------------------------------------ | | `--pricingCard-highlight-border-color` | Border/ring color of the highlight. | | `--pricingCard-check-color` | Feature list check icon color. |
      Everything else comes from the [card tokens](/components/card). ## Astro component
      | Prop | Type | Default | Description | | ------------- | ---------- | --------------- | ------------------------------------------------------- | | `name` | `string` | — | Tier name. Required. | | `price` | `string` | — | Price as text (`"$29"`, `"Free"`…). Required. | | `period` | `string` | `undefined` | Suffix after the price (`"/month"`). | | `description` | `string` | `undefined` | One-liner under the price. | | `features` | `string[]` | `[]` | Feature list, one check per entry. | | `ctaLabel` | `string` | `"Get started"` | CTA button text. | | `ctaHref` | `string` | `undefined` | CTA link; no CTA renders without it (or the `cta` slot).| | `highlighted` | `boolean` | `false` | Recommended tier: primary ring + primary CTA. | | `badge` | `string` | `undefined` | Badge text next to the name (`"Popular"`). | | `class` | `string` | `undefined` | Additional CSS classes. |
      | Slot | Description | | ----- | ----------------------------------------------------- | | `cta` | Replace the default CTA button with your own markup. |
      --- # Read Progress Bar > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/readprogressbar
      The `.readProgressBar` component creates a reading progress bar at the top of the page that represents the user's scroll position, growing as the user scrolls down and decreasing as they scroll up. This helps users understand their progress through the content and how much remains to be read. This component uses pure CSS when supported by the browser, and has a polyfill for [browsers that don't support](https://caniuse.com/?search=animation-timeline) `animation-timeline`. (Scroll this page to see the component in action.)
      | File | Description | Source | | ------------------------------- | ----------- | ----------- | | `component.readProgressBar.css` | All progress bar styles (`.readProgressBar`) | [Github][1] |
      [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.readProgressBar.css
      ## HTML ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens): | Property | Description | | -------------------------- | ------------------------------ | | `--readProgressBar-height` | Progress bar height. | | `--readProgressBar-color` | Progress bar background color. | ## Examples
      • ### Astro Component The live demo is the bar at the top of this very page (a fixed element can only be demoed once, so the examples below don't embed extra instances). ```astro --- import ReadProgressBar from '../components/ReadProgressBar.astro'; --- ```
      • ### HTML ```html ``` If you're using HTML and would like to us the polyfill for [browsers that don't support](https://caniuse.com/?search=animation-timeline) `animation-timeline`, add the following JavaScript to your code: ```js function throttle(func, delay) { let lastCall = 0; let timerId; return function (...args) { const now = new Date().getTime(); if (now - lastCall < delay) { clearTimeout(timerId); timerId = setTimeout(() => { lastCall = now; func.apply(this, args); }, delay); } else { lastCall = now; func.apply(this, args); } }; } function supportsScrollTimeline() { return CSS.supports("animation-timeline: scroll(root y)"); } function updateProgressBar() { const bar = document.querySelector(".readProgressBar > div"); if (!bar) return; const scrollTop = window.scrollY || document.documentElement.scrollTop; const docHeight = document.documentElement.scrollHeight - window.innerHeight; const progress = docHeight > 0 ? scrollTop / docHeight : 0; bar.style.width = progress * 100 + "%"; } function enableJSProgressBar() { const bar = document.querySelector(".readProgressBar > div"); if (!bar) return; // Remove any inline width set by CSS animation bar.style.animation = "none"; bar.style.animationTimeline = "none"; updateProgressBar(); const throttledUpdate = throttle(updateProgressBar, 10); window.addEventListener("scroll", throttledUpdate, { passive: true }); window.addEventListener("resize", throttledUpdate); } if (typeof window !== "undefined" && !supportsScrollTimeline()) { document.addEventListener("DOMContentLoaded", enableJSProgressBar); } ```
      --- # Section > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/section The `.section` component is the workhorse wrapper marketing pages are made of: a full-width band with vertical rhythm, an optional eyebrow/title/lede header, and background variants.

      Section content goes here.

      | File | Description | Source | | ---------------------------------------- | ---------------------------------- | ----------- | | `component.section.css` | All section styles | [Github][1] | | `Section.astro` | The Astro component | [Github][2] | | `--section-*` block in `theme.default.ui.css` | UI tokens (see table below) | [Github][3] |
      Depends on `.wrap` (`global.wrap.css`), part of the mCSS core: the header and the content each compose `.wrap` with their own [width preset](/docs/layout#wrap), so a section pairs a reading-width header (`.wrap-md`) with a wider content column (`.wrap-lg`) by default. [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.section.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/components/Section.astro [3]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground

      {eyebrow}

      {title}

      {lede}

      Section content goes here.

    `} baseClasses="section" controls={[ { heading: "Variation", items: [ { type: "select", name: "variant", label: "Variant", default: "", options: [ { label: "Default", value: "" }, { label: "Filled", value: "section-filled" }, { label: "Primary", value: "section-primary" }, ], }, ], }, { heading: "Content", items: [ { type: "text", name: "eyebrow", label: "Eyebrow", default: "Why mCSS", }, { type: "text", name: "title", label: "Title", default: "Everything is a section", }, { type: "text", name: "lede", label: "Lede", default: "Stack a few of these and you have a landing page.", }, ], }, ]} /> ## HTML ```html

    Why mCSS

    Everything is a section

    Stack a few of these and you have a landing page.

    ``` The header and content each compose `.wrap` with a [width preset](/docs/layout#wrap) (`.wrap-sm`, `.wrap-md`, `.wrap-lg`), so they can use different column widths. `wrap-md` on the header and `wrap-lg` on the content is the standard pairing; use `wrap-md` on the content too when it is text meant for reading (an FAQ, for example). ### Available modifiers
    | Class | Description | | ------------------------------- | ---------------------------------------------------------------------- | | `.section` | Default. Transparent background, `--section-spacing` vertical padding. | | `.section` + `.section-filled` | Subtle filled band. | | `.section` + `.section-primary` | Primary-colored band; text and links switch to the inverted colors. |
    ### Elements
    | Class | Description | | ------------------ | --------------------------------------------------------------------- | | `.section_header` | Centered eyebrow/title/lede block; composes `.wrap` + a width preset. | | `.section_content` | Content column; composes `.wrap` + a width preset. | | `.section_eyebrow` | Small uppercase kicker. | | `.section_title` | The `h2`. | | `.section_lede` | Intro paragraph. | | `.section_actions` | Centered, wrapping button row (see the closing-CTA recipe below). |
    ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens):
    | Property | Description | | ------------------------------------ | ---------------------------------------- | | `--section-spacing` | Vertical padding of the band. | | `--section-header-spacing` | Space between the header and content. | | `--section-eyebrow-color` | Eyebrow text color. | | `--section-lede-color` | Lede text color. | | `--section-filled-background-color` | Background of the filled variant. | | `--section-primary-background-color` | Background of the primary variant. | | `--section-primary-text-color` | Text color on the primary variant. | | `--section-primary-link-color` | Link color on the primary variant. | | `--section-primary-link-color-hover` | Link hover color on the primary variant. | | `--section-primary-eyebrow-color` | Eyebrow color on the primary variant. | | `--section-primary-lede-color` | Lede color on the primary variant. |
    ## Astro component
    | Prop | Type | Default | Description | | -------------- | -------- | ----------- | ------------------------------------------------------------------- | | `eyebrow` | `string` | `undefined` | Small uppercase kicker above the title. | | `title` | `string` | `undefined` | The section's `h2`. | | `lede` | `string` | `undefined` | Intro paragraph under the title. | | `variant` | `string` | `undefined` | `filled`, `primary`. | | `headerWrap` | `string` | `md` | Header column width preset: `sm`, `md`, `lg`. | | `contentWrap` | `string` | `lg` | Content column width preset: `sm`, `md`, `lg`. | | `headerClass` | `string` | `undefined` | Extra classes added to `.section_header`. | | `contentClass` | `string` | `undefined` | Extra classes added to `.section_content`. | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). |
    Any other attribute is passed through to the root `
    ` element (handy for `id` anchors). The default slot is the section content. ## Examples
    • Text and links flip to the inverted palette.

      ```astro --- import Section from "../components/Section.astro"; ---

      Text and links flip to the inverted palette.

      ```
    • By default the content column is wider (`wrap-lg`) than the header (`wrap-md`); set `contentWrap="md"` to keep long-form content at reading width:
      1
      2
      3
      ```astro
      ```
    • The closing CTA every marketing page ends on is not a separate component; it is a Section with a `.section_actions` row:
      ```astro
      ```
    --- # Social Media Links > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/socialmedia
    The `.socialMedia` component consists of an unordered list of links to social media accounts and their corresponding icons.
    | File | Description | Source | | --------------------------- | ----------- | ----------- | | `component.socialMedia.css` | All social links styles (`.socialMedia`) | [Github][1] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.socialMedia.css The following 4 sizes and 19 icons (18 social media platforms + 1 generic link icon) are available out of the box.
    | Platform | Icon | | ----------- | -------------------------------------------- | | Unknown URL | | | Discord | | | Facebook | | | Github | | | Instagram | | | Linkedin | | | Pinterest | | | Reddit | | | Snapchat | | | Telegram | | | Tiktok | | | X | | | Youtube | | | Twitch | | | Odysee | | | Minds | | | Substack | | | Bitchute | | | Bastyon | |
    ## Playground
  • {x}
  • {github}
  • {youtube}
  • `} baseClasses="socialMedia" snippets={{ x: { preview: xIcon, code: "[…]" }, github: { preview: githubIcon, code: "[…]" }, youtube: { preview: youtubeIcon, code: "[…]" }, }} controls={[ { heading: "Variation", items: [ { type: "select", name: "size", label: "Size", default: "", options: [ { label: "Default", value: "" }, { label: "Medium", value: "socialMedia-md" }, { label: "Large", value: "socialMedia-lg" }, { label: "Extra large", value: "socialMedia-xl" }, ]}, ]}, ]} /> ## HTML
    | Available modifiers | Description | | ---------------------------------- | -------------- | | `.socialMedia` | Default - 24px | | `.socialMedia` + `.socialMedia-md` | 32px | | `.socialMedia` + `.socialMedia-lg` | 44px | | `.socialMedia` + `.socialMedia-xl` | 64px |
    When using the HTML component you'll need to include the SVG icons "manually." All the icons are [available on Github](https://github.com/minimaldesign/mCSS/tree/main/src/assets/icons). If you'd like to use different icons, [Lucide](https://lucide.dev/) is a great resource.
    ## Astro component
    The Astro component ([Github](https://github.com/minimaldesign/mCSS/blob/main/src/components/SocialMedia.astro)) takes 3 props:
    | Prop | Type | Default | Description | | ------------ | -------- | --------- | ----------------------------------------------------------- | | `urls` | `array` | `[]` | An array of urls, or `{ url, label }` objects to override the accessible label (e.g. `"mCSS on GitHub"`). | | `size` | `string` | undefined | `md`, `lg`, `xl`. | | `newTab` | `boolean` | `true` | Open links in a new tab. Set to `false` to opt out. | | `class` | `string` | undefined | Additional CSS classes, useful for [helper classes](/docs/helpers). |
    ## Examples
    • ```astro --- import SocialMedia from "../../components/SocialMedia.astro"; --- ```
    • ```astro --- import SocialMedia from "../../components/SocialMedia.astro"; --- ```
    ### HTML examples --- # Tags > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/tags
    The `.tags` component styles a list of tags. It supports both list and inline layouts, is accessible by default, and customizable via CSS custom properties.
    | File | Description | Source | | -------------------- | ----------- | ----------- | | `component.tags.css` | All tags styles (`.tags`) | [Github][1] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.tags.css
    ## Playground
  • css
  • astro
  • basics
  • `} baseClasses="tags" controls={[ { heading: "Variation", items: [ { type: "select", name: "layout", label: "Layout", default: "", options: [ { label: "List", value: "" }, { label: "Inline", value: "tags-inline" }, ]}, ]}, ]} /> ## HTML ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens): | Property | Description | | ------------------------------- | --------------------------- | | `--tags-padding` | Tag padding. | | `--tags-font-size` | Tag font size. | | `--tags-color` | Tag text color. | | `--tags-background-color` | Tag background color. | | `--tags-background-color-hover` | Tag hover background color. | | `--tags-radius` | Tag border radius. | ### Available modifiers
    | Modifier | Description | | -------------- | ---------------------------- | | `.tags` | Default style (list layout). | | `.tags-inline` | Inline layout using flex. |
    ## Astro component
    | Prop | Type | Default | Description | | ------------- | ---------- | ----------- | ----------------------------------------------------------- | | `tagList` | `string[]` | `[]` | Array of tag strings to display. | | `variant` | `string` | `inline` | Inline or list layout. | | `url` | `string` | `/blog/tags` | Base URL to prepend to each tag. | | `sort` | `boolean` | `false` | Sort tags alphabetically; input order is preserved by default. | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). | | `ariaLabel` | `string` | `'Tags'` | Accessible label for the tag list. | | `data-testid` | `string` | `'tags'` | Test ID for testing purposes. |
    ## Examples
    • ### Astro example (frontmatter metadata) ```astro --- import Tags from '../components/Tags.astro'; const { frontmatter } = Astro.props; --- ```
    • ### HTML example (list layout) ```html ```
    --- # Testimonial > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/testimonial The `.testimonial` component is a quote with attribution: a `figure` wrapping mCSS's already-styled `blockquote`, plus an [Avatar](/components/avatar) byline. Pure HTML and CSS.

    We deleted four hundred lines of utility classes the week we switched. The cascade does the work now.

    | File | Description | Source | | ----------------------------------------------- | ------------------------------------- | ----------- | | `component.testimonial.css` | Layout + byline styles | [Github][1] | | `elements.text.css` | The `blockquote` styling (dependency) | [Github][2] | | `Testimonial.astro`, `Avatar.astro` | The Astro components | [Github][3] | | `--testimonial-role-color` in `theme.default.ui.css` | UI token | [Github][4] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.testimonial.css [2]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/elements.text.css [3]: https://github.com/minimaldesign/mCSS/tree/main/src/components [4]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/theme.default.ui.css ## Playground

    {quote}

    `} controls={[ { heading: "Content", items: [ { type: "text", name: "quote", label: "Quote", default: "We deleted four hundred lines of utility classes the week we switched.", }, ], }, { heading: "Byline", items: [ { type: "text", name: "name", label: "Name", default: "Sonny Rollins" }, { type: "text", name: "role", label: "Role", default: "Design lead, Acme", }, { type: "text", name: "initials", label: "Initials", default: "SM" }, ], }, ]} /> ## HTML ```html

    We deleted four hundred lines of utility classes the week we switched.

    ``` ### Custom properties
    | Property | Description | | -------------------------- | ---------------- | | `--testimonial-role-color` | Role text color. |
    The quote itself is themed by the `blockquote` element styles. ## Astro component
    | Prop | Type | Default | Description | | ----------- | ------------------------- | ----------- | ------------------------------------------------------------------------------ | | `name` | `string` | — | Who said it. Required (also feeds the Avatar initials when there is no photo). | | `role` | `string` | `undefined` | Title/company line. | | `avatarSrc` | `ImageMetadata \| string` | `undefined` | Photo for the Avatar. | | `class` | `string` | `undefined` | Additional CSS classes. |
    The default slot is the quote. ## Examples A wall of quotes is just testimonials on the [grid](/docs/layout#grid):
    • The copy-paste model means I actually own my components.

      Readable class names. Imagine that.

      Shipped our marketing site in a weekend.

      ```astro --- import Testimonial from "../components/Testimonial.astro"; ---

      The copy-paste model means I actually own my components.

      ```
    --- # Tiles > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/tiles The `.tiles` component lays out repeated, same-size items (cards, feature items, pricing tiers) in equal-height columns. It is container-responsive: columns appear as the nearest [size container](/docs/layout#layouts) crosses the size's thresholds, so the count answers the space the list actually has, not the viewport. The same markup is 2-up in this docs column and 4-up on the wide [blog index](/blog), at the same window size.
  • Columns come from the width of the column the list lives in, not the viewport.

  • Tiles in a row stretch to the same height, however long their content runs.

    Like the card right next to this one…

  • | File | Description | Source | | --------------------- | ----------------------------------------------------- | ----------- | | `component.tiles.css` | All tiles styles (`.tiles`), including the thresholds | [Github][1] | | `Tiles.astro` | The Astro component | [Github][2] |
    No dependencies. The [card](/components/card) blog index and the [feature grid](/components/featuregrid) pattern are both built on it. [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.tiles.css [2]: https://github.com/minimaldesign/mCSS/tree/main/src/components ## Playground
  • One

    Tiles share the row height.

  • Two

    Resize the window to watch the columns respond to this docs column.

  • Three

    The thresholds watch the container, not the browser window.

  • `} baseClasses="tiles" controls={[ { heading: "Layout", items: [ { type: "select", name: "size", label: "Tile size", default: "md", options: [ { label: "md (default)", value: "md", class: "" }, { label: "sm", value: "sm", class: "tiles-sm" }, ], } ], }, ]} /> ## HTML A `ul` with one tile per `
  • `. Items stretch, so tiles in a row share the same height. ```html
    ``` ### Available modifiers Sizes name the tile: each one is a set of container thresholds tuned to how much room that tile needs, and columns keep coming as the container crosses them. There is no column cap: the width of the column the list sits in is the cap.
    | Available modifiers | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `.tiles` | Card-sized tiles, roughly `17rem` of room each: 2 columns from a `36rem` container, then 3/4/5/6 every `20rem`. | | `.tiles` + `.tiles-md` | The default size, spelled out. | | `.tiles` + `.tiles-sm` | Small tiles (icon + short copy, roughly `13rem`): 2 columns from `28rem`, then every `16rem`, so columns appear sooner. |
    The thresholds are deliberately not the global `--md`/`--lg` viewport breakpoints: those can't see how wide the column is, and a tile's comfortable width is a property of the component, not of the page. Copy-it-you-own-it applies as usual: retune them in `component.tiles.css`. ### The container The column count responds to the nearest size container. Every [layout scaffold](/docs/layout#layouts) declares its main column as one, so inside a `.layout` page it just works. Outside a scaffold the wrapper is required: without a container ancestor the queries never match and the list stays single-column. ```html
    ``` ### Custom properties
    | Property | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--tiles-columns` | The count the thresholds resolved. Pin it on a hook class of your own to opt out (your unlayered CSS wins), or set it from your own `@container`/`@media` rules. |
    ```css /* Exactly three pillars, whatever the container says */ .pillars { --tiles-columns: 3; } ``` Gaps come from the [grid system](/docs/layout#grid)'s `--grid-column-gap` and `--grid-row-gap`, so tiles and grids share the same rhythm. ## Astro component Children go in the default slot; wrap each tile in an `
  • `.
    | Prop | Type | Default | Description | | ------------- | -------- | ----------- | ---------------------------------------------------------------------------------- | | `size` | `string` | `undefined` | Tile size: `sm` or `md`. Omitted is `md`. | | `class` | `string` | `undefined` | Additional CSS classes. Pin the count by setting `--tiles-columns` on one of them. | | `data-testid` | `string` | `'tiles'` | Test ID for testing frameworks. |
    ## Examples The blog-index recipe lives on the [card page](/components/card#clickable-cards), the icon + blurb marketing pattern on the [feature grid page](/components/featuregrid). --- # Table of Content > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/toc
    The `.toc` component is an "Astro only" client-side rendered component. It creates a table of content from the headings on the page it's on:
    By default, the TOC includes `H2`s and `H3`s (customizable via props) that have an ` id` attribute and links to them. It's possible to add multiple TOC on the same page, but this will duplicate Javascript and it's not recommended. If you do need to include more than one TOC, you'll need to add a unique `id` to each instance of the component.
    | File | Description | Source | | ------------------- | ----------- | ----------- | | `component.toc.css` | All TOC styles (`.toc`) | [Github][1] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.toc.css
    ## HTML This component's main functionality is to create a table of content via JavaScript. It just renders a nested `ul` and it's most useful as an Astro component. ### Custom properties The following custom properties are available in [`theme.default.ui.css`](/docs/default-theme#ui-tokens): | Property | Color | | ------------------- | ------------------------- | | `--toc-color` | Links color. | | `--toc-color-hover` | Links hover color. | | `--toc-spacing` | Links padding. | | `--toc-icon-size` | Icon size. | | `--toc-indentation` | Nested lists indentation. | ## Astro component
    | Prop | Type | Default | Description | | -------------- | --------- | ------------- | ----------------------------------------------------------- | | `id` | `string` | `toc` | Needed to add more than one TOC. | | `selector` | `string` | `main` | HTML element to scan for headings. | | `headings` | `array` | `['h2','h3']` | Heading levels to include. | | `baseUrl` | `string` | `''` | Optional URL to prepend `#id` with. | | `scrollOffset` | `integer` | `0` | Scroll position offset from the top.. | | `description` | `string` | `undefined` | Text above the TOC. | | `icon` | `string` | `''` | Optional inline SVG. (See example.) | | `class` | `string` | `undefined` | Additional CSS classes, useful for [helper classes](/docs/helpers). |
    ## Examples
    • ### TOC with only H2s ```astro --- import Toc from "../../components/Toc.astro"; --- ```
    • ### TOC with H2 — h4 and a ♥︎ icon #### A H4 just as an example ```astro --- import heart from "../../assets/icons/arrow-right-small.svg?raw"; import Toc from "../../components/Toc.astro"; --- ```
    --- # Toggle > Part of mCSS (mcss.dev). Rendered page: https://mcss.dev/components/toggle The toggle is a [CSS-only component](/docs/start#components) using the `.toggle` class on `` elements of the type `checkbox`.
    | File | Description | Source | | ----------------- | ----------- | ----------- | | `component.toggle.css` | All toggle styles (`.toggle`) | [Github][1] |
    [1]: https://github.com/minimaldesign/mCSS/blob/main/src/styles/framework/component.toggle.css ## Playground '} baseClasses="toggle" controls={[ { heading: "State", items: [ { type: "checkbox", name: "checked", label: "Checked", attr: "checked", default: true }, { type: "checkbox", name: "disabled", label: "Disabled", attr: "disabled", default: false }, ]}, ]} /> ## HTML
    • ```html ```