Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

All notable changes to mCSS. The framework follows the copy-it-you-own-it model: there is no package to update, so version numbers mark states of the repository you can copy from (each release is also a git tag).

## Unreleased

### Added

- **[Tiles](https://mcss.dev/components/tiles)** (`component.tiles.css`, `Tiles.astro`): CardList and FeatureGrid consolidated into one **container-responsive** list component, built on container queries. Columns appear as the nearest size container crosses the size's thresholds, so the count answers the width of the column the list sits in, not the viewport: the same markup is 2-up in a docs column and 4-up in a wide one at the same window size. Sizes name the tile, and each is its own threshold set: bare `.tiles` / `.tiles-md` fits card-sized tiles (2 columns from a `36rem` container, then every `20rem`), `.tiles-sm` fits icon + blurb items (2 from `28rem`, then every `16rem`). No column cap: the column's width is the cap, and columns keep coming (up to 6 enumerated) as the container allows. To pin a count, set `--tiles-columns` on a hook class of your own.
- Every `.layout` scaffold declares its main column as a named size container (`container: main / inline-size`); that is what Tiles responds to inside the scaffolds, and your own rules can use it too (`@container main (width >= …)`). Outside a scaffold, wrap a tiles list in `.tiles_container` (required: without a container ancestor the queries never match and the list stays single-column).
- The mcss.dev blog index goes 4-up on a laptop purely through a page-scoped `--layout-content-width` override (`.blog-index` in `page.blog.css`): the pattern to copy for wide listing pages.

### Changed

- mcss.dev page classes (`.blog`, `.docs`) moved from `<main>` to the `<body>`, next to the scaffold classes, so a page class can override layout tokens (they are consumed by `.layout_content`, which sits between the two).

### Breaking

- `.cardList` and `.featureGrid` are gone, replaced by [Tiles](https://mcss.dev/components/tiles): `<ul class="cardList grid" col="1" col-md="2" col-lg="3">` is now `<ul class="tiles">`, and `<ul class="featureGrid grid" …>` is `<ul class="tiles tiles-sm">` (`.featureItem` markup is unchanged). The lists no longer ride on the `.grid` attribute system (which is unchanged for your own grids); instead of picking column counts per viewport breakpoint, you pick the tile size and the container's width decides. `CardList.astro` / `FeatureGrid.astro` are replaced by `Tiles.astro` (`size="sm"` for the former FeatureGrid), and the `cols` prop is gone.

## 1.0.0 (2026-07-21)

The official launch. Everything from the 0.9 beta reviewed, restructured, and built out into a full component framework for websites.
Expand Down
4 changes: 2 additions & 2 deletions agents/css.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mCSS uses **native CSS cascade layers** (`@layer`) on top of an ITCSS-inspired f
| elements | `elements.*` | Bare HTML element styles (text, form, media) |
| global | `global.*` | Structural patterns (a11y, grid, layout, prose, wrap) |
| components | `component.*` | Library components (card, hero, notice, …), including CSS-only single-class ones (badge, button, toggle) |
| theme | `theme.*` | Swappable skins: token overrides + style rules, self-layered via `@layer theme`, activated by the consumer entry (never by `mcss.css`) |
| theme | `theme.*` | Swappable themes: token overrides + style rules, self-layered via `@layer theme`, activated by the consumer entry (never by `mcss.css`) |
| helpers | `help.*` | Utility overrides (colors, spacing, typography), all `!important`: beats everything, including unlayered CSS |

`page.*` is a file-naming convention only (page-specific styles, e.g. site `page.docs.css`); those files are plain unlayered consumer CSS, there is no `pages` layer.
Expand Down Expand Up @@ -58,7 +58,7 @@ To style a component from outside:
- **Its internals**: use the component's `<part>Class` props (`headerClass`, `titleClass`, …) to mix a class onto the part, or set the component's interface tokens on your own hook class. If neither exists yet, add the prop or token to the component. Do not reach in with a selector.
- **Bare HTML tags** (`.home_themer_formRow > button`), `.is-*` states, and ARIA attribute selectors are fine inside your own block.
- **Context blocks are not components**: a component may reference the environment it sits in (`@scope (.prose) to (.not-prose)` in `component.notice.css`, `:root.theme-dark`), but the context's own file must never name specific components (that's why `global.prose.css` lists only bare tags).
- **Theme files are the one sanctioned exception**: a `theme.*.css` file may select component classes from outside (`.card::after`, `.bt`) because a theme is by definition a skin over the whole system, versioned with the framework. Even there, prefer token overrides; reach for selectors only for what tokens can't express (pseudo-elements, `nth-child` rhythm, `text-decoration`).
- **Theme files are the one sanctioned exception**: a `theme.*.css` file may select component classes from outside (`.card::after`, `.bt`) because a theme is by definition a style over the whole system, versioned with the framework. Even there, prefer token overrides; reach for selectors only for what tokens can't express (pseudo-elements, `nth-child` rhythm, `text-decoration`).

### Token naming grammar

Expand Down
2 changes: 1 addition & 1 deletion agents/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ URLs for blog posts, docs, and component pages are derived from their file paths

## Theme Toggle

The light/dark toggle persists in **`localStorage`** (key `theme`). The inline script in `BaseLayout.astro` applies the class before paint to avoid FOUC; the toggle behavior itself lives in `ThemeToggle.astro` (self-contained; rendered twice, its module script runs once). Don't introduce a second color-scheme mechanism. (Theme files, `theme.*.css`, are full skins and unrelated to the light/dark toggle.)
The light/dark toggle persists in **`localStorage`** (key `theme`). The inline script in `BaseLayout.astro` applies the class before paint to avoid FOUC; the toggle behavior itself lives in `ThemeToggle.astro` (self-contained; rendered twice, its module script runs once). Don't introduce a second color-scheme mechanism. (Theme files, `theme.*.css`, are full themes and unrelated to the light/dark toggle.)
41 changes: 0 additions & 41 deletions dist/css/component.cardList.css

This file was deleted.

36 changes: 0 additions & 36 deletions dist/css/component.featureGrid.css

This file was deleted.

6 changes: 6 additions & 0 deletions dist/css/component.featureItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ xxl: 1920px
@layer components {
/* FEATURE ITEM */

.featureItem {
/* As a .tiles item the li is display:grid (equal-height rows); keep
the icon/title/text packed at the top instead of stretching apart */
align-content: start;
}

.featureItem_icon {
display: flex;
align-items: center;
Expand Down
136 changes: 136 additions & 0 deletions dist/css/component.tiles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*! mCSS v1.0.0 | MIT | https://mcss.dev */
/*
SETTINGS MEDIA QUERIES

https://github.com/argyleink/open-props/blob/main/src/props.media.css
https://open-props.style/#media-queries

Can't use var() inside media queries 🤦‍♂️
https://stackoverflow.com/questions/40722882/css-native-variables-not-working-in-media-queries/47212942#47212942
so breakpoints are not in settings.tokens.css

xxs: 240px
xs: 360px
sm: 480px
md: 768px
lg: 1024px
xl: 1440px
xxl: 1920px
*/

/* https://github.com/postcss/postcss-mixins */

/* Example */

/* truncate single line of text and adds … */

@layer components {
/* TILES */

/*
Equal tiles in container-responsive columns: cards, feature items,
pricing tiers, anything repeated at one size. The column count answers
"how wide is the column I live in", not "how wide is the viewport": the
.layout scaffolds declare their main column as a size container; outside
a scaffold, wrap the list in a .tiles_container (or any inline-size
container). The wrapper is required there: with no container ancestor
the queries never match and the list stays single-column.

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 (no count cap; the column's width is the cap). A bare
.tiles uses the md thresholds (card-sized tiles); .tiles-sm fits small
icon + blurb tiles, so its columns appear sooner. The thresholds are
deliberately not the global viewport breakpoints: those can't see how
wide the column is. To pin a count yourself, set --tiles-columns on a
hook class of your own (unlayered CSS beats the rules below), or write
your own @container rules.
*/

.tiles {
--tiles-columns: 1;
display: grid;
grid-template-columns: repeat(var(--tiles-columns), 1fr);
grid-column-gap: var(--grid-column-gap);
grid-row-gap: var(--grid-row-gap);
margin: 0;
padding: 0;
list-style: none;
}

.tiles > li {
/* grid items stretch, so tiles in a row share the same height */
display: grid;
}

/* md, the default (.tiles-md exists as the explicit spelling): a tile
keeps roughly 17rem of room */
@container (width >= 36rem) {
.tiles {
--tiles-columns: 2;
}
}

@container (width >= 56rem) {
.tiles {
--tiles-columns: 3;
}
}

@container (width >= 76rem) {
.tiles {
--tiles-columns: 4;
}
}

@container (width >= 96rem) {
.tiles {
--tiles-columns: 5;
}
}

@container (width >= 116rem) {
.tiles {
--tiles-columns: 6;
}
}

/* sm: a tile keeps roughly 13rem of room. Same specificity as the base
rules, so these must stay after them (a smaller tile never means fewer
columns at the same width). */
@container (width >= 28rem) {
.tiles-sm {
--tiles-columns: 2;
}
}

@container (width >= 44rem) {
.tiles-sm {
--tiles-columns: 3;
}
}

@container (width >= 60rem) {
.tiles-sm {
--tiles-columns: 4;
}
}

@container (width >= 76rem) {
.tiles-sm {
--tiles-columns: 5;
}
}

@container (width >= 92rem) {
.tiles-sm {
--tiles-columns: 6;
}
}

/* Fallback container for tiles used outside the .layout scaffolds */
.tiles_container {
container-type: inline-size;
}

}
7 changes: 7 additions & 0 deletions dist/css/global.layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ xxl: 1920px
flex: 1;
}

/* The main column is a size container: container-responsive components
(Tiles) pick their column count from it instead of the viewport.
Named, so custom rules can target it with @container main. */
.layout_content_main {
container: main / inline-size;
}

/* Single centered reading column: articles, blog posts, legal pages. */
.layout-centered .layout_content {
display: grid;
Expand Down
3 changes: 1 addition & 2 deletions dist/css/mcss.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
@import url(./component.breadcrumbs.css) layer(components);
@import url(./component.button.css) layer(components);
@import url(./component.card.css) layer(components);
@import url(./component.cardList.css) layer(components);
@import url(./component.faq.css) layer(components);
@import url(./component.featureGrid.css) layer(components);
@import url(./component.featureItem.css) layer(components);
@import url(./component.field.css) layer(components);
@import url(./component.fieldRow.css) layer(components);
Expand All @@ -25,6 +23,7 @@
@import url(./component.socialMedia.css) layer(components);
@import url(./component.tags.css) layer(components);
@import url(./component.testimonial.css) layer(components);
@import url(./component.tiles.css) layer(components);
@import url(./component.toc.css) layer(components);
@import url(./component.toggle.css) layer(components);
@import url(./elements.form.css) layer(elements);
Expand Down
8 changes: 6 additions & 2 deletions dist/css/settings.ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ xxl: 1920px

:root {
/* Color scheme (light/dark), toggled by the .theme-light / .theme-dark
classes below. Unrelated to theme files, which are full skins. */
classes below. Unrelated to theme files, which are full themes. */
--csstools-color-scheme--light: initial;
color-scheme: light dark;

Expand Down Expand Up @@ -621,7 +621,7 @@ xxl: 1920px

}

/* FeatureGrid */
/* FeatureItem */

:root {
--featureItem-icon-box-size: var(--lg3);
Expand Down Expand Up @@ -993,6 +993,10 @@ xxl: 1920px

}

/* Tiles: no tokens. The container thresholds live in component.tiles.css
(container queries can't read custom properties), and --tiles-columns
is set there; pin it on a hook class of your own to opt out. */

/* TOC */

:root {
Expand Down
22 changes: 11 additions & 11 deletions dist/css/theme.wireframe.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ xxl: 1920px
/* WIREFRAME THEME */

/*
A hand-drawn wireframe skin: grayscale palette, rounded type, wobbly
A hand-drawn wireframe theme: grayscale palette, rounded type, wobbly
borders, paper-cutout shadows, and a traced-twice pencil stroke on
cards and buttons. Mostly token overrides; the style rules at the
bottom cover what tokens can't express. See theme.default.css for how
Expand Down Expand Up @@ -322,10 +322,10 @@ xxl: 1920px
*/
@supports (rotate: calc(sibling-index() * 1deg)) {
.grid_item,
.cardList > li,
.tiles > li,
.bt,
.featureItem,
.card:not(.grid_item .card):not(.cardList .card) {
.card:not(.grid_item .card):not(.tiles .card) {
--sketch-seed: sibling-index();
}

Expand Down Expand Up @@ -395,18 +395,18 @@ xxl: 1920px
(rotate: random(per-element, -1deg, 1deg)) {
.card,
.featureItem {
rotate: -0.36081deg;
rotate: -0.36081deg;
rotate: random(element-scoped, -0.4deg, 0.4deg);
rotate: random(per-element, -0.4deg, 0.4deg);
}

.card::after,
.bt::after {
rotate: 0.14152deg;
rotate: 0.14152deg;
translate: -1.23108px
0.98843px;
translate: -1.23108px
0.98843px;
rotate: random(element-scoped, -0.6deg, 0.6deg);
rotate: random(per-element, -0.6deg, 0.6deg);
translate: random(element-scoped, -1.5px, 1.5px)
random(element-scoped, -1.5px, 1.5px);
translate: random(per-element, -1.5px, 1.5px)
random(per-element, -1.5px, 1.5px);
}
}
}
Loading
Loading