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
5 changes: 3 additions & 2 deletions e2e/next-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"build": "next build",
"verify:build": "bash ../../scripts/verify/build-consumer.sh",
"verify:assert": "bash ../../scripts/verify/assert-consumer.sh e2e/next-app/.next e2e/next-app/scripts/assert-build.ts",
"verify": "vp run @animus-ui/next-app#verify:build && vp run @animus-ui/next-app#verify:assert",
"start": "next start"
"verify": "vp run @animus-ui/next-app#verify:build && vp run @animus-ui/next-app#verify:tsc && vp run @animus-ui/next-app#verify:assert",
"start": "next start",
"verify:tsc": "bash ../../scripts/verify/tsc-consumer.sh e2e/next-app"
},
"dependencies": {
"@animus-ui/next-plugin": "workspace:*",
Expand Down
6 changes: 6 additions & 0 deletions e2e/next-app/scripts/assert-build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AssertionError,
assertClassNameFormat,
assertConditionsInsideLayers,
assertKeyframesExtracted,
assertLayerOrder,
assertNoEmotionImports,
Expand Down Expand Up @@ -104,6 +105,11 @@ async function main(): Promise<void> {

assertNoPlaceholders(css);

// Guardrail G2 (modern-css-surface): condition at-rules must nest inside a
// named @layer block. Non-vacuous here — the imported test-ds Card emits raw
// @container / @media / @supports rules into this build's CSS.
assertConditionsInsideLayers(css);

// Keyframes extracted through the webpack adapter — the fixture declares
// `animations = keyframes({ fadeIn, pulse })` in src/ds.ts; the assertion
// proves both blocks land in @layer anm-global, both animation-name refs
Expand Down
5 changes: 3 additions & 2 deletions e2e/vite-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
"verify:build": "bash ../../scripts/verify/build-consumer.sh",
"verify:assert": "bash ../../scripts/verify/assert-consumer.sh e2e/vite-app/dist e2e/vite-app/scripts/assert-build.ts",
"verify:dry-run": "bash ../../scripts/verify/dry-run-worker.sh e2e/vite-app '@animus-ui/vite-app' e2e/vite-app/dist animus-vite-canary '@animus-ui/vite-app#verify:build'",
"verify": "vp run @animus-ui/vite-app#verify:build && vp run @animus-ui/vite-app#verify:assert && vp run @animus-ui/vite-app#verify:dry-run",
"verify": "vp run @animus-ui/vite-app#verify:build && vp run @animus-ui/vite-app#verify:tsc && vp run @animus-ui/vite-app#verify:assert && vp run @animus-ui/vite-app#verify:dry-run",
"preview": "vite preview",
"cf:deploy": "wrangler deploy",
"cf:upload": "wrangler versions upload",
"cf:dry-run": "wrangler deploy --dry-run"
"cf:dry-run": "wrangler deploy --dry-run",
"verify:tsc": "bash ../../scripts/verify/tsc-consumer.sh e2e/vite-app"
},
"dependencies": {
"@animus-ui/system": "workspace:*",
Expand Down
33 changes: 33 additions & 0 deletions e2e/vite-app/scripts/assert-build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AssertionError,
assertClassNameFormat,
assertConditionsInsideLayers,
assertKeyframesExtracted,
assertLayerOrder,
assertNoEmotionImports,
Expand Down Expand Up @@ -164,6 +165,38 @@ async function main(): Promise<void> {
assertClassNameFormat(css, { prefix: 'animus-' });
assertGlobalBaseline(css);

// Guardrail G2 (modern-css-surface): every @container / @supports /
// non-breakpoint @media condition at-rule must nest inside a named @layer
// block. Runs NON-VACUOUSLY here — the test-ds Card (raw @container/@media/
// @supports) and the app Card (registered `_motionReduce` alias) both emit
// condition rules into this dist.
assertConditionsInsideLayers(css);

// Container-unit emission pin (inc 11, spec "Container-relative units on
// scale-typed properties"): the test-ds Card authors `gap: '2cqi'` on a
// strict space-scale prop inside a nested @container block — the unit
// string must ship verbatim (the resolver emits it; minifiers may reformat
// the prelude but not the declaration value).
if (!css.includes('gap:2cqi') && !css.includes('gap: 2cqi')) {
throw new AssertionError(
'container-unit emission pin: expected `gap:2cqi` (verbatim container unit on a strict scale prop) in the dist CSS',
{ probe: 'gap:2cqi' }
);
}

// Built-in condition composite witness (inc 06): the app Card authors
// `_osDark` WITHOUT registering it — it must resolve through the DEFAULT
// built-in set across the full registry → manifest → plugin → engine wire.
if (
!css.includes('prefers-color-scheme:dark') &&
!css.includes('prefers-color-scheme: dark')
) {
throw new AssertionError(
'built-in condition pin: expected an unregistered `_osDark` block to emit `@media (prefers-color-scheme: dark)` via the default built-in set',
{ probe: 'prefers-color-scheme:dark' }
);
}

// Keyframes extracted through the rollup (Vite) adapter — fixture declares
// `animations = keyframes({ fadeIn, pulse })` in src/ds.ts; the assertion
// proves both blocks land in @layer anm-global, both animation-name refs
Expand Down
13 changes: 13 additions & 0 deletions e2e/vite-app/src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,18 @@ export const Card = ds
border: '1px solid',
borderColor: 'border',
p: 16,
// Registered condition-alias block (modern-css-surface inc 03). Resolves
// through the app's `_motionReduce` registration to
// `@media (prefers-reduced-motion: reduce)` — the aliased-emission proof.
_motionReduce: {
transition: 'none',
},
// UNREGISTERED built-in (inc 06 composite witness): this app never
// registers `_osDark` — it resolves through the DEFAULT built-in set via
// the full SystemBuilder → manifest → plugin glue → engine path. The
// assert lane pins its emission.
_osDark: {
borderColor: 'border',
},
})
.asElement('div');
4 changes: 2 additions & 2 deletions e2e/vite-app/src/components/Pulse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const Pulse = ds
color: 'text',
px: 16,
py: 8,
borderRadius: 4,
borderRadius: '4px',
animationName: animations.pulse,
animationDuration: '2s',
animationTimingFunction: 'ease-in-out',
Expand All @@ -20,7 +20,7 @@ export const Fade = ds
color: 'text',
px: 16,
py: 8,
borderRadius: 4,
borderRadius: '4px',
animationName: animations.fadeIn,
animationDuration: '1s',
animationFillMode: 'forwards',
Expand Down
7 changes: 7 additions & 0 deletions e2e/vite-app/src/ds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ export const {
.addGroup('text', typography)
.addGroup('surface', { ...color, ...border })
.addGroup('positioning', positioning)
// Condition alias registry (modern-css-surface inc 03). Registered here so
// aliased condition blocks (e.g. `_motionReduce`) resolve during THIS app's
// extraction — proving the manifest `conditionAliases` field flows through
// the full plugin glue (loadSystemConfig → analyze args → engine adapter).
.addConditions({
_motionReduce: '@media (prefers-reduced-motion: reduce)',
})
.build();

export const globalStyles = createGlobalStyles({
Expand Down
45 changes: 45 additions & 0 deletions openspec/specs/arch-css-structural-gates/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# arch-css-structural-gates Specification

## Purpose
TBD - created by archiving change modern-css-surface. Update Purpose after archive.
## Requirements
### Requirement: Condition at-rules gated inside layer blocks

Consumer verification lanes SHALL run a structural assertion that every `@container`, `@supports`, and non-breakpoint `@media` at-rule in assembled dist CSS sits inside a named `@layer` block, and the vite-app and next-app assert lanes SHALL invoke it non-vacuously (their dists contain condition at-rules).

#### Scenario: Vite-app lane runs the containment check

- **WHEN** the following command is run

```bash
vp run "@animus-ui/vite-app#verify:assert"
```

- **THEN** it exits 0 with `assertConditionsInsideLayers` invoked against a dist whose CSS contains at least one `@container`, one `@supports`, and one non-breakpoint `@media` rule
- **AND** a hoisted (outside-`@layer`) condition at-rule in that dist causes the command to exit non-zero

#### Scenario: Next-app lane runs the containment check

- **WHEN** the following command is run

```bash
vp run "@animus-ui/next-app#verify:assert"
```

- **THEN** it exits 0 with the same assertion invoked against the Next dist CSS

### Requirement: Registered property rules gated in the variables part

The showcase assert lane SHALL pin the registered-`@property` wire end-to-end: the dist CSS contains the theme's registered `@property` rule, positioned before the first `@layer` block.

#### Scenario: Showcase lane pins @property placement

- **WHEN** the following command is run

```bash
vp run "@animus-ui/showcase#verify:assert"
```

- **THEN** it exits 0 with the dist CSS containing `@property --current-bg` before the first `@layer` block
- **AND** removing the registration from the showcase theme (or the wire dropping it) causes the command to exit non-zero

91 changes: 91 additions & 0 deletions openspec/specs/container-query-support/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# container-query-support Specification

## Purpose
TBD - created by archiving change modern-css-surface. Update Purpose after archive.
## Requirements
### Requirement: Container condition block emission

Style objects SHALL accept raw `@container` condition strings as block keys, and each such block SHALL emit a `@container` rule wrapping the component's class selector inside the same `@layer` block that owns the component's base rule.

#### Scenario: Basic container condition

- **WHEN** a style object contains `'@container (min-width: 400px)': { fontSize: '18px' }`
- **THEN** the emitted CSS contains `@container (min-width: 400px) { .ClassName { font-size: 18px; } }`
- **AND** the `@container` rule appears inside the same `@layer` block as the component's base rule

#### Scenario: Token and shorthand resolution inside container blocks

- **WHEN** a style object contains `'@container (min-width: 400px)': { bg: 'primary', p: 16 }`
- **THEN** the emitted container rule contains `background-color: var(--color-primary)` and `padding: 1rem`

#### Scenario: No rule emitted for empty container block

- **WHEN** a style object contains `'@container (min-width: 400px)': {}`
- **THEN** no `@container` rule is emitted for that key

### Requirement: Named container conditions

Container condition keys SHALL support an optional container name, and the name SHALL be preserved verbatim in the emitted at-rule prelude.

#### Scenario: Named container query

- **WHEN** a style object contains `'@container card (min-width: 400px)': { display: 'grid' }`
- **THEN** the emitted CSS contains `@container card (min-width: 400px) { ... }`

### Requirement: Container establishment via declarations

Container establishment properties SHALL be accepted as ordinary style declarations: `containerType`, `containerName`, and the `container` shorthand SHALL emit their corresponding CSS declarations on the establishing element's rule.

#### Scenario: Establishing a named container

- **WHEN** a style object contains `{ containerType: 'inline-size', containerName: 'card' }`
- **THEN** the emitted base rule contains `container-type: inline-size;` and `container-name: card;`

#### Scenario: Container shorthand

- **WHEN** a style object contains `{ container: 'card / inline-size' }`
- **THEN** the emitted base rule contains `container: card / inline-size;`

### Requirement: Container conditions compose with selector blocks

Container condition blocks SHALL admit nested selector-alias and raw-selector blocks, and the emitted rule SHALL nest the composed selector inside the `@container` at-rule.

#### Scenario: Selector alias inside a container block

- **WHEN** a style object contains `'@container (min-width: 400px)': { _hover: { bg: 'primary' } }`
- **THEN** the emitted CSS contains `@container (min-width: 400px) { .ClassName:hover { background-color: var(--color-primary); } }`

### Requirement: Container-relative units on scale-typed properties

Strict scale-typed props (props bound to a token scale without `strict: false`) SHALL accept the six container-relative length units — `cqw`, `cqi`, `cqh`, `cqb`, `cqmin`, `cqmax` — as string values carrying a numeric prefix, at the type level, in both plain and responsive-value-map positions, and each such value SHALL be emitted verbatim. Admission SHALL NOT widen a strict scale prop to arbitrary strings: only these six unit suffixes with a numeric prefix are accepted; other unit strings and bare (numberless) suffixes SHALL remain rejected, and scale keys SHALL continue to resolve.

#### Scenario: Container unit on a strict scale prop

- **WHEN** a style object contains `p: '2cqi'` on a strict space-scale prop
- **THEN** the style object typechecks
- **AND** the emitted CSS contains `padding: 2cqi;` (the unit string emitted verbatim)

#### Scenario: Container unit in a responsive-map slot

- **WHEN** a style object contains `p: { _: 8, sm: '2cqi' }` on a strict space-scale prop
- **THEN** the style object typechecks, with the scale key `8` resolving to its token value and `2cqi` carried into the `sm` slot verbatim

#### Scenario: Non-container unit string rejected

- **WHEN** a style object contains `p: '2vw'` on a strict space-scale prop
- **THEN** the style object fails to typecheck (viewport units are not admitted; strictness is preserved)

#### Scenario: Bare container-unit suffix rejected

- **WHEN** a style object contains `p: 'cqi'` (no numeric part) on a strict space-scale prop
- **THEN** the style object fails to typecheck

### Requirement: Registered container condition aliases

Condition aliases registered with a `@container` value SHALL resolve as block keys to the registered container condition.

#### Scenario: Container alias in a style object

- **WHEN** the system is configured with `.addConditions({ _cardSm: '@container card (min-width: 400px)' })` and a style object contains `_cardSm: { display: 'grid' }`
- **THEN** the emitted CSS contains `@container card (min-width: 400px) { .ClassName { display: grid; } }`

Loading
Loading