Record boxel-ui component-selection and field-nesting rules#103
Draft
burieberry wants to merge 2 commits into
Draft
Record boxel-ui component-selection and field-nesting rules#103burieberry wants to merge 2 commits into
burieberry wants to merge 2 commits into
Conversation
Five conventions, all found while fixing the boxel.ai hero meta strip (CS-12316), where a spacing ticket turned into a structural one. Authored into both trees per the double-authoring invariant in README: skills/boxel-ui-guidelines/references/* and Skill/boxel-ui-guidelines.md. - Don't neutralize a component, pick the variant. A <style scoped> block that zeroes a component's padding/background/border means the wrong component or variant was chosen. Includes the arg-portability warning (@as/@href are Button's; Pill uses @tag + ...attributes) and the hrefless-anchor-renders-as-disabled gotcha, split into the two cases it can mean (link genuinely unavailable -> @disabled + aria-disabled; optional field -> non-anchor element). - Button's documented API was incomplete: the chromeless link kinds (link/link-primary/link-muted) and @as were both missing. - Primitive grays as text color: concrete dark-mode failure mode, since a mid-gray reads as the safe neutral choice and isn't one. - :deep()/display:contents are for host-generated DOM. A wrapper FieldDef you own gets deleted, not flattened. Two tells: a containsMany of wrappers each holding one item, and reaching across a scoped-style boundary into a child's own classes. - Responsive values: reassign custom properties in the @container block instead of re-declaring the consuming rules per breakpoint. Plus a typography note (take a whole semantic role group rather than assembling font-size + a hand-computed calc() line-height) and five checklist items. Examples use meta-link / meta-strip naming rather than badge, matching what the code they came from ended up called. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Workspace Sync Dry-Run CompleteDry-run completed successfully - no changes were made Dry-Run Results |
The gotcha was written around boxel-ui's current internals — "Button sets no aria-disabled, add it yourself", "@disabled on an anchor is identical DOM". CS-12305 will falsify all of that, and the worst of it is the instruction: it would keep teaching a workaround for a fixed bug. Restructured so the durable part leads and the perishable part is fenced: - The rule is now "an optional @href is a decision, not a detail" — Case 1 (link exists, unavailable) vs Case 2 (optional field, plain label), decided by what the data model intends. That holds at any boxel-ui version. - The mechanism moved into a dated, ticket-linked version note that tells the reader to delete it, and itself, once CS-12305 lands. - Added why this matters more after the fix, not less: today a Case 2 item written as Case 1 is silent to assistive tech; afterwards it actively announces a plain label as an unavailable link. - Checklist item reworded off the current behaviour too. CS-12305 now lists the three files to clean up when that bullet ships, so the note can't rot unnoticed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five conventions learned while fixing the boxel.ai hero meta strip (CS-12316) — a spacing ticket that turned into a structural one. Each rule below is something the review of that change actually caught, not a hypothetical.
Authored into both trees per the double-authoring invariant in the README (
skills/boxel-ui-guidelines/references/*andSkill/boxel-ui-guidelines.md), since nothing syncs them.The rules
use-boxel-ui-components.mdButton's real API: link kinds and@aswere both undocumenteduse-boxel-ui-components.mduse-boxel-design-tokens-for-theming.md:deep()-flattening themdelegated-render-control.mduse-container-queries-not-viewport-units.mdPlus a typography note (take a whole semantic role group rather than assembling
font-size+ a hand-computedcalc()line-height) and five checklist items.Notes on two of them
The hrefless-anchor gotcha is written as a decision, not a rule.
Buttonstylesa.boxel-button:not([href])as a disabled link —opacity: 0.5, disabled color,pointer-events: none— so@as='anchor'with a conditionally-empty@hrefproduces a faded element whose state is never stated anywhere. The fade often looks like intentional de-emphasis, which is how it survives review. The guidance splits it into the two things it can mean: a link that genuinely exists but is unavailable (pass@disabledandaria-disabled, which Button's anchor branch does not set) versus an optional field where some items are plain labels (render a non-anchor element). They render nearly identically, so the only real difference is what the code claims is true.The field-nesting rule sharpens existing guidance rather than adding to it.
delegated-render-control.mdalready documents flattening host-generated plural-field wrappers withdisplay: contents. What was missing is the check before reaching for it: if the wrapper is a FieldDef you own, delete it. Two tells that it isn't a real grouping level — acontainsManyof wrappers each holding exactly one item, and writing a selector in the parent's<style scoped>that reaches a class defined in a child field's own template.Also worth knowing
Button's documented@kindlist was wrong by omission — it listed primary/secondary/muted/destructive/text-only but notlink/link-primary/link-muted, which are exactly the variants you want for text that should read as a link rather than a control. That omission is arguably what caused the original code to reach for aPilland strip it bare.Skill/tree has no delegated-render coverage at all — no mention ofplural-field,:deep(), ordisplay: contentsanywhere in it. The field-nesting rule went in there as a standalone section rather than a mirror. That gap is bigger than this PR and probably wants its own ticket.Examples use
meta-link/meta-stripnaming rather thanbadge, matching what the code they came from ended up called. Pre-existingbadgeusages (fittedsmall-badgesizes,FittedCard'sbadgeLeft/badgeRowslots,Pill's own description) are untouched — those are real API surface.Draft because these are conventions the team has to agree to, not just docs.
🤖 Drafted by Claude (Opus 5).
🤖 Generated with Claude Code