refract v0.1 · beta
Subsystems / Effects

Effects

Shadow, blur, opacity, transitions and z-index as format-neutral tokens — multi-layer values with colours referenced by path, live on hover.

raw slice the input
live render hover the cards
refracted: the two surfaces use dt-effects-surface-card / -focusable — hovering fires the real transition + shadow tokens. Flip the preset: the shadow depth & motion recompute from that theme.

Overview

The effects subsystem owns the visual-treatment tokens — shadow, blur, opacity, z-index and transitions. Each is a plain property (a base value plus named variants) emitted as a :root custom property, and each can be referenced by a surface recipe that composes several into one class. It's a “regular” subsystem — values pass straight through (no palette-style synthesis like colors).

note: border & outline geometry — radius, borderWidth, outline — lives in the dedicated borders subsystem, not effects.

Properties

Five token families. shadow / transitions are structured object-leaf properties — authored as objects/arrays of leaves (the only accepted string is the "none" keyword); opacity / zIndex are unitless numbers; blur is a number → px (or a string).

PropertyValue typeEmitted variableDescription
shadowShadowValue--dt-effects-shadow-<v>Structured box-shadow layers (or "none") — see below.
transitionsTransitionValue--dt-effects-transitions-<v>Structured transition parts (or "none") — see below.
opacitynumber--dt-effects-opacity-<v>Unitless 01.
zIndexnumber--dt-effects-zindex-<v>Unitless stacking index.
blurnumber | string--dt-effects-blur-<v>Blur radius; numbers → px. Consumed by the recipe blur prop (→ filter).

The base value is emitted at --dt-effects-<prop> and resolvable at token path effects.<prop>; each variant at …-<variant> / effects.<prop>.<variant>.

Structured shadow & transitions

shadow and transitions are authored as leaves, never raw CSS strings. A single flat leaf sits at the property top level and is the base; variants / responsive / modes hold full leaf values (a multi-layer shadow / multi-part transition is an array of leaves in a variant or mode). There is no base key; a property with only variants has an implicit "none" base. The only accepted string is the keyword "none" (no shadow / no transition) — any other string throws. A shadow stays translucent by referencing a translucent colour — an alpha colour variant (see colours) — not a shadow-level opacity field.

Shadow layer (ShadowLayerInput)
FieldTypeDescription
offsetX / offsetYnumber | stringOffsets. A bare number is a deferred length (px by default, per the units config); a string is a pinned length ("1px", "0.5rem"). Default 0.
blurnumber | stringBlur radius — deferred length, or a pinned string.
spreadnumber | stringSpread radius — deferred length, or a pinned string.
colorstringA colors.* token refvar(--dt-colors-…) (never a raw colour). Point it at an alpha colour variant for a translucent shadow.
insetbooleanRenders an inset shadow.
Transition part (TransitionPartInput)
FieldTypeDescription
propertystringThe animated property (e.g. "all", "transform").
durationnumberDuration (ms).
timingFunctionstringEasing keyword / function.
delaynumberDelay (ms).
effects.ts
colors: {
  // a translucent shadow ink: an `alpha` colour variant (10% opaque)
  ink: { base: "#0e1117", variants: { soft: { modifiers: [{ alpha: 10 }] } } },
},
effects: {
  shadow: {
    // a flat leaf at the top level IS the base; translucency comes from the colour it refs
    offsetY: 1, blur: 2, color: "colors.ink.soft",
    variants: {
      none: "none",   // the only accepted string
      md: { offsetY: 6, blur: 16, color: "colors.ink.soft" },
      // an array in a variant = a multi-layer shadow
      lg: [{ offsetY: 10, blur: 20, color: "colors.ink.soft" }, { offsetY: 2, blur: 4, color: "colors.ink.soft" }],
    },
  },
  transitions: {
    property: "all", duration: 160, timingFunction: "ease-out",
    variants: { fast: { property: "all", duration: 90, timingFunction: "ease" } },
  },
  opacity: { base: 1, variants: { muted: 0.6, disabled: 0.4 } },
  zIndex:  { base: 1, variants: { dropdown: 1000, modal: 1300 } },
  blur:    { base: 0, variants: { sm: 4, lg: 16 } },
}
dist/theme.css · emitted :root
:root {
  --dt-colors-ink-soft: rgba(14, 17, 23, 0.1);
  --dt-effects-shadow: 0px 1px 2px var(--dt-colors-ink-soft);
  --dt-effects-shadow-lg: 0px 10px 20px var(--dt-colors-ink-soft), 0px 2px 4px var(--dt-colors-ink-soft);
  --dt-effects-transitions: all 160ms ease-out;
}

Recipes — surface

A surface recipe composes effect variants into one class. Each recipe prop names a variant of the matching property — but the recipe key differs from the token key, so the interpreter maps it:

Recipe propReferences propertyCSS declarationNotes
boxShadowshadowbox-shadow
transitiontransitionstransitionRecipe key is singular.
opacityopacityopacity
zIndexzIndexz-index
blurblurfilterCompoundfilter: blur(var(--…)). The wrap lives in the adapter; no embedded var() in the Model.

Recipe declarations resolve to token-path refs — boxShadow: "lg"effects.shadow.lgvar(--dt-effects-shadow-lg). Recipes also take states (:hover, [disabled], …) and a responsive list (@media overrides) — see the Concepts pages.

effects.recipes.ts
effects.recipes: {
  surface: {
    card: {
      boxShadow: "md", transition: "base",
      states: [{ state: "hover", boxShadow: "lg" }],
      responsive: [{ breakpoint: "lg", query: "min", boxShadow: "lg" }],
    },
    focusable: {
      boxShadow: "sm", opacity: "muted", zIndex: "modal", blur: "sm",
    },
  },
}
dist/theme.css · emitted
.dt-effects-surface-card {
  box-shadow: var(--dt-effects-shadow-md);
  transition: var(--dt-effects-transitions);
}
.dt-effects-surface-card:hover { box-shadow: var(--dt-effects-shadow-lg); }
@media (min-width: 1024px) {
  .dt-effects-surface-card { box-shadow: var(--dt-effects-shadow-lg); }
}
.dt-effects-surface-focusable {
  box-shadow: var(--dt-effects-shadow-sm);
  opacity: var(--dt-effects-opacity-muted);
  z-index: var(--dt-effects-zindex-modal);
  filter: blur(var(--dt-effects-blur-sm));
}
Technical documentation for @theme-registry/refract · all output is compiled client-side by the real library (CSS adapter). The chrome is theme-aware; the render panes carry each preset's own world. · MIT licensed.