dssl / native /spec /06-properties.md
EHS-Studio's picture
Claude Sonnet 5.5
v0.16.3: native DSSL spec, and the seven bugs writing it found
9da4a9b
|
Raw History Blame Contribute Delete
14.8 kB

6. Properties

A declaration prop: value becomes zero or more output declarations. An output declaration is either an ordinary CSS declaration { prop, value }, or an engine-native declaration whose property starts with @ (6.8).

6.1 Lookup order

  • PRP-1. For each declaration, the resolver does the first of these that applies:
    1. If the value contains !important (VAL-15): report the error, emit nothing.
    2. If prop starts with --: it is a custom property (TOK-15, TOK-16).
    3. If prop is a semantic property (the names in 6.2 to 6.7, and appendix A): call that property's expansion. It returns its own output declarations.
    4. If prop is animation or animation-name: resolve the value with bareIdents: true and unitless per the unitless list. (Motion names are bare words, and must not be reported as unknown values.)
    5. If the value is fluid ... and prop is in the fluid list: VAL-12 to VAL-14.
    6. If prop is not a known CSS property (appendix A): report unknown property "<prop>", hint = did-you-mean over the known properties and the semantic property names. Emit nothing.
    7. Otherwise: one output declaration with the same property and the value resolved by chapter 3.

The known-property list holds every property that CSS defines, plus a few vendor ones. It is generated from the engine's vocabulary module (appendix A).

Each output declaration also carries the line of the source declaration.

.a { colour: red }

6.2 Size and flow words

  • PRP-2. size: w [h]. Two declarations width and height. The height defaults to the width. Both are lengths.
  • PRP-3. circle: s. width: s, height: s, border-radius: 50%.
  • PRP-4. z: n is z-index: n (a plain number). grow, shrink are flex-grow, flex-shrink (a plain number, default 1 when empty). basis: l is flex-basis: l.
  • PRP-5. cover [position] is position: <position or absolute> and inset: 0. sticky [top] is position: sticky and top: <top or 0>.
  • PRP-6. truncate (no value) is overflow: hidden, text-overflow: ellipsis, white-space: nowrap.
  • PRP-7. visually-hidden (no value) is position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0, in that order.

6.3 Row, column, grid, stack, place, center

Place words map to two values, (cross, main):

word cross main
center center center
start start flex-start
end end flex-end
between center space-between
around center space-around
evenly center space-evenly
stretch stretch flex-start
baseline baseline flex-start
  • PRP-8. row: words and column: words start with display: flex and flex-direction: row|column, then read the words in order:
    • gap N: gap: N (a length). Missing N is the error gap needs a value.
    • wrap / nowrap: flex-wrap.
    • reverse: the direction becomes row-reverse or column-reverse.
    • inline: display becomes inline-flex.
    • a place word: see PRP-9.
    • anything else: error unknown word "<w>" in <row|column>, hint = did-you-mean over gap center start end between around evenly stretch baseline wrap nowrap reverse inline.
  • PRP-9. Each place word has an axis role. between, around, evenly are main (they write justify-content). stretch and baseline are cross (they write align-items). center, start, end are both: they write whichever axes have not been written yet by an earlier word in this declaration.
    • A word that writes an axis a second time overrides it, with a warning: two words in <dir> write <prop>: "<new>" overrides "<old>", or when the same word, "<w>" is written twice in <dir> - the second copy wins.
    • A both word with both axes already taken writes nothing and warns "<w>" has nowhere to go: justify-content came from "<a>", align-items came from "<b>" (or the "written twice" form when it is the same word).
  • PRP-10. grid: words. Words gap N and rows N are taken out first. The rest are the column spec:
    • nothing: error grid needs columns, columns are none,
    • one integer N: repeat(N, 1fr),
    • auto-fill L or auto-fit L: repeat(auto-fill, minmax(L, 1fr)) with L a length,
    • otherwise each word is a length, joined by spaces. The output is display: grid, grid-template-columns, then gap if given, then grid-template-rows if given (repeat(N, 1fr) for an integer, a length otherwise).
  • PRP-11. stack: word (default center) is display: grid, place-items: <cross> <main> and @stack: "1". An unknown word is the error unknown place "<w>" in stack and only @stack is emitted.
  • PRP-12. place: word is place-items and place-content, both <cross> <main>. Unknown: unknown place "<w>".
  • PRP-13. center: both|x|y (default both). both: place-items: center center and place-content: center center. x: justify-items: center, justify-content: center. y: align-items: center, align-content: center. Unknown: unknown center axis "<v>".
  • PRP-14. align: center|start|end|stretch|baseline is align-items with that word. Unknown: unknown align "<v>".
.toolbar { row: gap 8 between center }
.board   { grid: 3 gap 12 }
.pane    { stack: center }
.hero    { center: both }

The first rule gives display: flex; flex-direction: row; gap: 8px; justify-content: space-between; align-items: center.

6.4 Text and look

  • PRP-15. font: size[/line-height] [color]. DSSL's font: is not the CSS shorthand. It writes font-size (a length, default 16), then line-height (a plain number) if a / part is there, then color if more words follow. If the value looks like the CSS shorthand (it contains ", ' or ,, or its first word is one of italic oblique bold bolder lighter normal small-caps condensed expanded caption icon menu message-box small-caption status-bar system-ui serif sans-serif monospace, or it is a weight 100..900 followed by a sized word), the result is the error font: "<v>" looks like the CSS font shorthand, but DSSL's font: means size, line height and colour and nothing is emitted.
  • PRP-16. fluid: a, b[, v0, v1] is font-size through VAL-12 to VAL-14.
  • PRP-17. shadow and radius: TOK-17, TOK-18.
  • PRP-18. text-stroke: width [color] [inside], or none. It writes -webkit-text-stroke: <width> <color> (width in px if bare, color default currentcolor), and paint-order: stroke fill unless inside is given. none writes -webkit-text-stroke: 0 and paint-order: normal. A missing or non-numeric width is the error text-stroke needs a width first.
  • PRP-19. outline-shape: width [color], or none. It writes one filter made of four drop-shadow(x y 0 color) terms for (w,0), (-w,0), (0,w), (0,-w). none is filter: none. A missing width is the error outline-shape needs a width first.
  • PRP-20. rotate: v. A bare number n is n degrees. x|y|z n and four numbers also get deg. Anything else is resolved as a unitless value.

6.5 transition

transition: part, part, ... or none. Each part is duration [easing] [property].

  • PRP-21. The duration is a number with an optional ms or s unit. A bare number is milliseconds. The easing is an easing name (appendix A) or a cubic-bezier(...) or steps(...) call. The property is a known CSS property, or one of the deform names (bend, bend-y, bend-up, fold, stretch, which mean transform), and defaults to all. The part becomes <property> <duration> <easing>, with the easing defaulting to ease. Parts are joined by , .
  • PRP-22. If a part does not start with a time but contains one (the CSS habit, color .2s ease), the error is transition: the CSS form (property first) is not DSSL, with a hint that rewrites the part in DSSL order. A part with no time at all is the error transition duration "<d>" is not a time. An unknown property is unknown transition property "<p>". A part with an error is dropped. If no part survives, nothing is emitted.

6.6 gradient

gradient: layer + layer + .... The first layer is on top. Each layer is a kind sentence optionally followed by layer options.

  • PRP-23. Layers are split on a top-level + that has white space on both sides.
  • PRP-24. Layer options may follow the sentence, in any order, each at the end of the text: tile W [H] (background size, bare numbers are px, H defaults to W), offset X Y (background position) and one of no-repeat, repeat-x, repeat-y.
  • PRP-25. If the layer (after options) starts with a CSS gradient function, url(, image-set( or -webkit-, it is resolved as an ordinary value. Otherwise it is a sentence.
  • PRP-26. A sentence is [repeating] linear|radial|conic <head> <stops> or mesh <base>, <blob>, .... The output is the CSS function linear-gradient(...), radial-gradient(...), conic-gradient(...) (with repeating- first when asked).
  • PRP-27. Stops are separated by -> or by commas. Each is a color (a token, hex, named color, color function, alpha form) with up to two trailing positions, or a lone position (a midpoint hint). Bare numbers in positions are px (degrees for conic), except 0 which stays 0. A stop word that is a plain word but not a color name is the error gradient stop "<s>" is not a color.
  • PRP-28. A linear head is an angle (a bare number is degrees, 45deg also works) or to <side> [<side>]. Default 180deg. A radial head is made of circle, ellipse, position words, lengths and at. A conic head is from <angle> and/or at <position>. Anything else is an error saying what is not part of the gradient. An interpolation space (in oklch, in hsl longer hue, ...) may end the head.
  • PRP-29. mesh takes a base color first (an item without at), then blobs color at X Y [size S] (size defaults to 60%). Each blob is radial-gradient(circle farthest-side at X Y, color, transparent S). The base is linear-gradient(base, base). The blobs come first, the base last.
  • PRP-30. Errors: gradient needs a kind: ..., gradient needs at least two stops, "<s>" is a midpoint, so it has to sit between two colours, a gradient stop is missing its color, mesh needs a base colour and at least one blob, mesh item "<i>" needs a position, mesh needs a base colour first (an item without "at").
  • PRP-31. gradient: writes background-image marked as coming from a gradient, plus background-size, background-position and background-repeat only when some layer set one (with auto, 0 0 and repeat for the layers that did not).
  • PRP-32. gradient-text: layers writes the same image, then background-clip: text, -webkit-background-clip: text, color: transparent, -webkit-text-fill-color: transparent.
  • PRP-33. gradient-flow: duration [easing] [alternate|alternate-reverse] or none writes @gradient-flow with { duration (ms), easing, direction }. none and an empty value write @gradient-flow: null. The easing default is linear and the direction default is normal.
.stripes { gradient: repeating linear 45, #fff 0 10, color.ink 10 20 }
.blend   { gradient: linear 90 in oklch, #ff0055 -> #00d4ff }
.sheen   { gradient: radial at top left, #fff/40 -> transparent + linear 135, #123 -> #456 }

6.7 yield

yield: name name ... hands properties back to ordinary CSS.

  • PRP-34. The names are separated by spaces or commas and lowercased. Each MUST be a known CSS property or a semantic property name, otherwise the error yield: "<n>" is not a CSS property and nothing is emitted. An empty list is the error yield needs the properties to hand back. none writes @yield: null. Otherwise it writes @yield with the list of names.
  • PRP-35. For a name that is a shorthand, its longhands are handed back too (padding hands back padding-top and the rest).

6.8 Engine-native declarations

Declarations whose property starts with @ are not CSS. They describe behavior that needs more than a computed style. Each is produced by exactly one DSSL property and consumed by the feature it belongs to.

Output property Produced by Value Chapter
@animate animate: a spec record, or null 7
@stagger stagger: milliseconds or null 7
@overlap overlap: allow, deny, auto 8.6
@stack stack: "1" 6.3
@bend, @bend-y, @bend-up, @fold bend, bend-y, bend-up, fold degrees 8.5
@stretch stretch: { factor, axis } 8.5
@bend-around bend-around: degrees 8.5
@gradient-flow gradient-flow: spec record or null 6.6
@texture texture: list of layers or null 8.1
@aura-glow, @aura-particles the aura family list of layers or null 8.2
@slider slider: spec record 8.3
@tilt tilt: spec record or null 8.4
@yield yield: list of names or null 6.7
  • PRP-36. Engine-native declarations are not CSS and a host MUST NOT pass them to the style system as properties. A Level 3 implementation combines them with the rule for each feature. The default combination is the CSS one: for each name, the last in cascade order wins. Exceptions are @animate (all of them are kept, 7.2) and @yield (names accumulate, 5.11).

6.9 behavior and use

behavior focus-ring {
  outline: 2 solid color.accent
  outline-offset: 2
}

.btn { use focus-ring }
  • PRP-37. A top-level behavior name { ... } defines a named block of declarations and nested blocks. A second block with the same name is the error behavior "<n>" is defined twice. Behaviors are collected before the walk, so a behavior may be used above its definition. behavior is allowed only at the top level.
  • PRP-38. use name inside a block replays the behavior's children there, as if they had been written in that place, with the same context. An unknown name is unknown behavior "<n>" with a did-you-mean over the defined names. A behavior that uses itself, directly or through others, is behavior cycle involving "<n>".
  • PRP-39. The declarations of a behavior are resolved at the place they are used, so tokens, conditions and layers there apply to them.