dssl / dssl-vscode /lib /props-docs.js
EHS-Studio's picture
Claude Sonnet 5.5
v0.16.0: round three
10656db
Raw History Blame Contribute Delete
13.5 kB
/* props-docs.js - the DSSL vocabulary with one-line docs.
* Feeds completions and hovers. Semantic vocabulary entries carry real
* explanations; passthrough CSS properties get a short reminder that they
* pass through untouched. */
export const SEMANTIC = {
row: 'flex row in one line: `row: gap space.4 between center` sets display:flex, flex-direction, gap, justify-content, align-items. Warning: it writes display - put explicit display: before it, not after.',
column: 'flex column in one line: `column: gap 2`. Same display caveat as row: the shorthand wins if it comes later in the rule.',
grid: 'grid layout in one line: `grid: 3 gap 12` = 3 equal columns with a 12px gap.',
stack: 'everything stacked and centered: `stack: center`. Flex column + center on both axes.',
center: 'alignment that cannot silently no-op - the engine promotes the element to a grid formatting context when needed: `center: both`.',
size: 'width and height in one line: `size: 40 20` = 40 wide, 20 tall. `size: 40` = both.',
shadow: 'box shadow from the shadow token scale: `shadow: sm | md | lg | xl`. Custom: `shadow: 0 2 4 ink/10, 0 4 12 ink/8`.',
radius: 'border-radius, bare number or token: `radius: md`, `radius: full`, `radius: 14`.',
gradient: 'gradient as a sentence: `gradient: linear 135, color.accent -> color.ok`. Also `repeating linear 45, #fff 0 10, #000 10 20` (stripes), `linear 90 in oklch, a -> b` (blend colour space), `a -> 30% -> b` (midpoint), `a 0 30%, b 30% 100%` (hard bands), `mesh #fff4e6, #ff7a1a at 15% 20%, #3949ab at 80% 70%` (soft blobs on a base), and layers joined with ` + ` (first on top; each can end with `tile 24`, `offset 4 4`, `no-repeat`, and can be a CSS `radial-gradient(...)`). A stop that is not a color is a compile error.',
'gradient-text': 'clips the gradient grammar into the glyphs: `gradient-text: linear to right, color.accent, color.ink`.',
'gradient-flow': 'the gradient drifts forever through WAAPI: `gradient-flow: 6s alternate`. Holds still under reduced-motion.',
bend: 'bows the element in 3D. Positive tips the top away, negative leans toward you. Bare numbers are degrees. Composes into the one transform channel.',
'bend-y': 'sideways bow. `bend-y: -18`.',
'bend-up': 'rocks back on its planted bottom edge, drawbridge-style. `bend-up: 14`.',
fold: 'swings up over its top edge like a closing page. `fold: 14`.',
stretch: 'cartoon squash-and-stretch that keeps volume: `stretch: 0.94` squeezes to 0.94 wide and honestly grows to 1/0.94 tall.',
'bend-around': 'curves a container\u2019s CHILDREN along a circular arc instead of deforming one element: `bend-around: -22` opens into a smile. Caps at \u00b1170\u00b0. Children keep their own hover bends.',
animate: 'run a motion (or several: `animate: blink 300 infinite, waddle 900 infinite alternate`): `animate: name duration [easing] [delay N] [jitter N] [iterations N] [infinite] [alternate] [essential]`. Example: `animate: pulse 1600 infinite alternate`. `jitter` gives each element its own random delay, `essential` runs it even when the visitor prefers reduced motion. Works on ::before and ::after too. Run one from JS with `DSSL.play(el, name, opts)`.',
yield: 'hand properties back to plain CSS: `yield: padding-top padding-bottom`. The engine writes inline styles, which beat a stylesheet; a yielded property is not written, so your CSS decides it, with no !important. `yield: padding` covers every padding-*, `yield: none` clears it. DSSL.lint reports CSS that loses (css-loses).',
'outline-shape': 'an outline that follows the SHAPE (a sprite\u2019s alpha), not its box: `outline-shape: 3 color.ink`. Four hard drop-shadows, square corners. Uses `filter`, so it replaces another filter on the same rule. `none` clears it.',
'text-stroke': 'an outline round text, outside the letters: `text-stroke: 6 color.ink` (writes -webkit-text-stroke and paint-order: stroke fill). Add `inside` for a centred stroke, `none` to clear.',
stagger: 'choreographed children: `stagger: 70` fires each child\u2019s animation 70ms after its older sibling.',
transition: 'per-property transition: `transition: 200`, or `transition: 200 snap transform`. Honors reduced-motion.',
overlap: 'the anti-overlap policy: `auto` (engine shoves translucent elements apart), `deny` (this one never sits on anything), `allow` (exempt). Nothing below 100% opacity overlaps anything unless you say so.',
truncate: 'one-line ellipsis. Expands to overflow hidden, nowrap, text-overflow ellipsis.',
'visually-hidden': 'hidden from eyes, visible to screen readers.',
cover: 'fills the containing block absolutely.',
use: 'apply a behavior: `use focus-ring`. Behaviors are named rule groups declared with `behavior name { }`.',
glass: 'frosted glass: `glass: 18`, `glass: 24 saturate 1.8 tint white/12`. Blurs what is behind the element and adds a faint wash of the text colour. Plain CSS, so `dssl build` keeps it. `glass: none` clears it.',
tilt: 'follows the pointer in 3D: `tilt: 10`, `tilt: 12 lift 1.04 perspective 700 speed 0.2 invert glare`. Runtime only. `glare` keeps --dssl-gx and --dssl-gy pointing at the pointer so you can draw a shine. Ignores touch, flat under reduced motion.',
texture: 'a generated texture layered ON TOP of the element\u2019s own background: `texture: grain`, `texture: wood scale 6 intensity 0.8`, `texture: grain, scanlines`. 73 presets (grain, paper, marble, wood, carbon, halftone, ...) or build your own from a generator (noise, bump, lines, dots, grid, checker, weave, carbon, waves, hex) plus settings (scale, intensity, contrast, octaves, color, color2, blend, stretch, contour, ...). Wood: `texture: oak figure 0.8 knots 2 planks 120` (rings generator, ten species). `symmetrical` mirrors the tile so it never shows a seam. Open the designer to see them all.',
aura: 'light and particles around an element, with NO presets: you describe it. `aura: glow #ff7a00 30 flicker 0.3, particles #fd0 -> #f60 count 60 shape flame glowing behind`. Replaces both slots. `aura: none` clears them.',
'aura-glow': 'just the glow slot: `aura-glow: color.accent 24 pulse 1600`. Positional colour and radius, then settings (around SHAPE, inner, rays N, spin N, ripple ms, nohalo, z N, always, intensity, pulse, depth, flicker, texture(...), seed, essential). `aura-glow: none` clears only the glow.',
'aura-particle': 'just the particles slot: `aura-particle: #7cf -> transparent 40 up size 6 life 800 speed 30 from bottom`. Settings: around SHAPE (the outline `from edge` follows), palette, spin, wind, orbit, shape (heart, drop, bubble, snowflake, confetti, leaf...), z N (own canvas layer), count, size, life, speed, dir, spread, gravity, drag, turbulence, from, shape, grow, glowing, behind, blend, texture(...), seed, essential.',
'aura-particles': 'the same as `aura-particle`.',
'glow-particle': 'the particles slot with every particle glowing (additive light): `glow-particle: #fff 30 out`.',
'glow-particles': 'the same as `glow-particle`.',
'aura-glow-particle': 'both slots at once, settings shared: `aura-glow-particle: #ff7a00 28 pulse 900 count 50 up`. A glow layer AND a glowing particles layer.',
'aura-glow-particles': 'the same as `aura-glow-particle`.',
slider: 'styles `<input type="range">` through the WebKit/Blink (`::-webkit-slider-*`) and Firefox (`::-moz-range-*`) pseudo-elements at once: `slider: track 6 color.line radius full, fill color.accent, thumb 22 color.paper, ring 3 color.accent, glow color.accent 14, ticks 10`. Add `graph 20 45 70 90 60` for a histogram slider.',
seen: 'state that fires ONCE when the element first scrolls into view. Reveal-on-scroll with zero JavaScript. Composes with stagger. Cannot be combined with pseudo-elements.',
};
export const CSS = {
display: 'passthrough CSS. Note: semantic shorthands (row, column, grid, stack) also write display \u2014 order matters, and the compiler warns when a shorthand clobbers an explicit display.',
position: 'passthrough CSS: static, relative, absolute, fixed, sticky.',
width: 'passthrough CSS. Bare numbers are pixels.',
height: 'passthrough CSS. Bare numbers are pixels.',
margin: 'passthrough CSS. Shorthand with 1-4 values.',
padding: 'passthrough CSS. Shorthand with 1-4 values.',
background: 'passthrough CSS, but prefer `gradient:` sentences for gradients.',
color: 'text color. Values: token (color.ink), hex, CSS color name, alpha mix (color.ink/85).',
border: 'passthrough CSS shorthand: `border: 1 solid color.line`.',
'border-radius': 'passthrough CSS \u2014 prefer `radius:`.',
'font-size': 'passthrough CSS. Bare numbers are pixels.',
'font-family': 'passthrough CSS. Bare multi-word font names are legal since v0.5.',
'line-height': 'passthrough CSS.',
'letter-spacing': 'passthrough CSS.',
'text-align': 'passthrough CSS: left, center, right, justify.',
'text-transform': 'passthrough CSS: uppercase, lowercase, capitalize.',
'white-space': 'passthrough CSS: nowrap, pre, pre-wrap.',
opacity: 'passthrough CSS. Below 100% the overlap policy wakes up (see `overlap`).',
transform: 'passthrough CSS \u2014 deform (bend/stretch/fold) composes into this channel.',
translate: 'the transform channel the engine itself uses for overlap escapes; animating translate never fights the engine.',
rotate: 'rotation. A bare number is degrees: `rotate: 5` is 5deg. `5deg`, `0.1turn` and `x 30` pass through.',
scale: 'passthrough CSS.',
filter: 'passthrough CSS: blur(10), brightness(1.1).',
'backdrop-filter': 'passthrough CSS: blur(10).',
outline: 'passthrough CSS: `outline: 2 solid color.accent`.',
cursor: 'passthrough CSS: pointer, default, not-allowed.',
'z-index': 'passthrough CSS.',
overflow: 'passthrough CSS: hidden, auto, scroll, clip.',
'object-fit': 'passthrough CSS: cover, contain.',
gap: 'passthrough CSS \u2014 or let row:/column: set it inline.',
'flex-direction': 'passthrough CSS \u2014 row:/column: set it.',
'flex-wrap': 'passthrough CSS: wrap, nowrap.',
'justify-content': 'passthrough CSS \u2014 row:/column: can set it.',
'align-items': 'passthrough CSS \u2014 row:/column: can set it.',
'grid-template-columns': 'passthrough CSS \u2014 `grid: 3` is the sugar.',
'grid-template-rows': 'passthrough CSS.',
'align-content': 'passthrough CSS.',
'box-shadow': 'passthrough CSS \u2014 prefer `shadow:`.',
inset: 'passthrough CSS.',
'aspect-ratio': 'passthrough CSS: `aspect-ratio: 16 9`.',
'user-select': 'passthrough CSS: none stops text selection (pairs with pointer-events: none for veils).',
'pointer-events': 'passthrough CSS: none lets clicks and selection fall through overlays.',
'scrollbar-color': 'passthrough CSS for themed scrollbars.',
'scrollbar-width': 'passthrough CSS.',
'accent-color': 'passthrough CSS for native controls.',
'will-change': 'passthrough CSS.',
content: 'pseudo-element content, required for ::before/::after. Empty is auto-corrected to "".',
top: 'passthrough CSS.',
right: 'passthrough CSS.',
bottom: 'passthrough CSS.',
left: 'passthrough CSS.',
};
export const STATES = {
hover: 'engine-native hover: real events, enters and leaves, re-evaluates descendants too.',
focus: 'engine-native focus state.',
'focus-visible': 'keyboard-focus only. The focus-ring behavior\u2019s home.',
active: 'mouse/touch down.',
press: 'alias for active.',
pressed: 'alias for active.',
disabled: 'matches while the element is disabled.',
checked: 'matches while the element is checked.',
empty: 'matches an element with no children.',
target: 'matches when the element is the URL fragment target.',
seen: 'fires once, first time the element scrolls into view. Zero-JS reveal-on-scroll; composes with stagger.',
};
export const AT_RULES = {
'@media': 'conditions: `@media (max-width: 860)`, sugar forms `@media dark`, `@media light`, `@media reduced-motion`, bare widths, `<=` ranges.',
'@container': 'evaluates against the nearest ancestor (or self) with a container-type: `@container (min-width: 480)`.',
'@signal': 'the JS bridge: `@signal menuOpen { ... }` re-evaluates when DSSL.signal(\u0027menuOpen\u0027, true) fires. Mirrors onto data-dssl-signal-* attributes. Also `@signal screen in (menu, shop)`, `not in (a, b)` and `!=`. `DSSL.signal(name, value, el)` sets it for one subtree.',
'@layer': 'cascade layers: `@layer base { }`, `@layer components { }`, `@layer utilities { }`. Cascade is (layer, source order) \u2014 no specificity, no !important.',
};
export const BLOCKS = {
tokens: 'define design tokens: `tokens { color { ink: #16161d } radius { md: 14 } space { 9: 96 } }`. Numeric keys are legal. Referenced as color.ink, radius.md, space.9. Trailing /n adds alpha.',
behavior: 'a named rule group you can `use` anywhere: `behavior focus-ring { focus-visible { ... } }`.',
motion: 'a named keyframe animation, run with `animate:`: `motion pulse { from { } to { } }`.',
fonts: 'load fonts without raw CSS: `fonts { Fraunces: 500 700 800 "Hanken Grotesk": 400 700 italic Brand: from "/fonts/brand.woff2" 400 }`. Google families become a <link> (and an @import in `dssl build`); `from` writes @font-face. Settings: weights or a range (100..900), italic, display swap|block|fallback|optional.',
pin: 'must-win declarations. The cascade ranks tokens < base < components < utilities < pin. This is the ONLY way to force a win \u2014 there is no !important.',
};
export const PROPERTY_NAMES = [...Object.keys(SEMANTIC), ...Object.keys(CSS)];