UX-DSL
XS0px

Shadows

Elevation and depth tokens.

Define shared depth. Let components choose the treatment.

Responsibility: maintain shared shadow treatments and their responsive behavior. Shadows describe visual depth or an inset effect. A preset can contain several layers, remain stable or change across breakpoints. Its number is a key, not a pixel value, a z-index or a guarantee that higher numbers always look stronger.

Define Shadows in the theme JSON

{
  "breakpoints": { "xs": 0, "md": 768 },
  "shadows": {
    "0": "none",
    "2": "xs(0 2px 4px rgba(0, 0, 0, 0.12)) md(0 6px 16px rgba(0, 0, 0, 0.18))",
    "inset": "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
  }
}

These are illustrative values. Inspect the active configuration and overrides. Shared defaults are supplied for omitted presets. Shadow 0 explicitly means no shadow. Each configured value can be plain CSS or a responsive progression with a base value.

UXDSL

.card {
  box-shadow: shadow(2);
}
.inset-panel {
  box-shadow: shadow(inset);
}

Equivalent CSS for the card

:root { --uxdsl__shadow__2: 0 2px 4px rgba(0, 0, 0, 0.12); }
@media (min-width: 768px) {
  :root { --uxdsl__shadow__2: 0 6px 16px rgba(0, 0, 0, 0.18); }
}
.card { box-shadow: var(--uxdsl__shadow__2); }

The component selects a preset. At 768px the shared variable changes; at later breakpoints that value persists until another override applies. This is a discrete transition, not automatic interpolation. elevation(2) is an alias for shadow(2); neither changes stacking order.

Layers, inset effects and system references

{
  "shadows": {
    "3": "0 2px 4px rgba(0, 0, 0, 0.06), 0 4px 10px rgba(0, 0, 0, 0.14)"
  }
}

Preserve comma-separated layers and the commas inside color functions. A shadow describes offsets, blur, optional spread, color and optionally inset. Use the preset with box-shadow; do not assume its grammar is valid for text-shadow or filter: drop-shadow().

Presets may reference existing space(), density(), color() or palette() tokens. Keep those dependencies when intentional. Density is the preferred component spacing abstraction; shadow offsets and blur do not automatically need Density.

Change the correct scope

  • For one card, select another suitable existing Shadow preset.
  • For all consumers of a shared treatment, edit its theme definition and rebuild or apply the updated theme through generateThemeCss(nextTheme).
  • For an intentional independent effect, use native CSS. Use box-shadow: none to remove a shadow locally, or shadow(0) to stay connected to the configured zero preset.

Do not replace shadow(2) with today's computed value when the component should follow future theme changes. Changing the token updates every consumer after the configuration is applied. Inspect active overrides, clipping ancestors, backgrounds, focus indicators and interaction states.

One engine at build time, runtime and in the preview

PostCSS, runtime and the demo share the Shadow compiler and responsive resolver. Defaults are maintained in the shared module and emitted into postcss-uxdsl/theme/default-shadows.uxdsl by the generation script. The previous runtime supported literal values; responsive values now resolve through the same engine as PostCSS.

/* Legacy input remains supported in the same compilation. */
@theme {
  shadow-2: xs(0 2px 4px rgba(0, 0, 0, 0.12)) md(0 6px 16px rgba(0, 0, 0, 0.18));
}

JSON entries override matching legacy declarations, which override defaults. Include legacy definitions in every compilation that needs them; they no longer leak between builds through a global cache. Undefined Shadow references now report errors instead of silently selecting a fallback. Validation is not a complete CSS grammar or accessibility checker.

The live editor below starts from the active theme and shared defaults. Its changes affect only the preview, not your source JSON. It uses the real viewport and shared inspector. An invalid edit retains the last valid preview; Reset restores the active theme.

AI implementation guide

How an AI agent should use Shadows

Responsibility: maintain shared depth treatments and their responsive behavior. Preserve intent, not just the current computed value.

  • Inspect the effective shadows configuration, legacy imports, breakpoints and referenced color or spacing tokens before selecting a preset.
  • Reuse an appropriate configured shadow(key) or elevation(key) for box shadows. Do not infer visual strength from a numeric key or confuse elevation with z-index.
  • Preserve layers, nested color functions, inset flags, units and token references. Do not split shadow expressions with a simple comma-based parser.
  • Do not copy resolved shadow values into a component that should remain connected to a shared preset.
  • Change a shared preset only when all its consumers should follow. Select another preset or use native CSS for a deliberate local exception.
  • Define new presets before use. Unknown references are errors; check existing presets before adding a duplicate.
  • Use the shared generator and inspector for runtime and previews. Update source configuration rather than hand-editing generated CSS.
  • Verify just below, at and just above transitions, intermediate persistence, multiple consumers, states, themes, clipping, focus visibility and invalid-update recovery.

Decision rule: Choose the shared Shadow treatment in the component. Define its appearance and responsive progression in the theme. Use local CSS for intentional exceptions.

Agent reasoning example

For “make only this card less elevated,” inspect the actual preset values and choose an appropriate existing treatment for that card; leave the shared definition intact. For “soften shadow-2 across the product,” edit that preset, preserve its layers and references, apply the theme and inspect every affected consumer and breakpoint.

Interactive Demo

Shared engine playground

Actual viewport: 0px. Resize the browser to test transitions. The preview uses the active theme and shared engine defaults. Edits below are scoped to this demo and do not save source JSON.

Shadow presets

Shadow 0
box-shadow: shadow(0);
none
Shadow 1
box-shadow: shadow(1);
0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.1)
Shadow 2
box-shadow: shadow(2);
0 1px 2px rgba(0, 0, 0, 0.05), 0 2px 6px rgba(0, 0, 0, 0.12)
Shadow 3
box-shadow: shadow(3);
0 2px 4px rgba(0, 0, 0, 0.06), 0 4px 10px rgba(0, 0, 0, 0.14)
Shadow 4
box-shadow: shadow(4);
0 4px 6px rgba(0, 0, 0, 0.08), 0 10px 15px rgba(0, 0, 0, 0.16)
Shadow 5
box-shadow: shadow(5);
0 10px 15px rgba(0, 0, 0, 0.1), 0 20px 25px rgba(0, 0, 0, 0.2)