Skip to content

Add raw CSS value input mode to SliderRow component - #447

Merged
jackgranatowski merged 2 commits into
mainfrom
claude/design-token-vars-defaults-9896i6
Jun 29, 2026
Merged

Add raw CSS value input mode to SliderRow component#447
jackgranatowski merged 2 commits into
mainfrom
claude/design-token-vars-defaults-9896i6

Conversation

@jackgranatowski

Copy link
Copy Markdown
Contributor

Summary

Extends the SliderRow component to support direct CSS expression input (variables, calc, clamp, etc.) alongside numeric slider controls. This allows users to override numeric values with dynamic CSS expressions while maintaining a fallback to slider mode.

Key Changes

  • SliderRow component (configurator/src/components/inputs/SliderRow.svelte):

    • Added three new optional props: rawDefault, currentRaw, and onRawSet to support raw CSS value handling
    • Introduced userRawMode state to toggle between slider and raw input modes
    • Added isRawOverride derived state to auto-detect CSS expressions (var, calc, clamp, min, max, env)
    • Implemented conditional rendering: shows text input for raw CSS when in raw mode or when override is a CSS expression; shows slider otherwise
    • Added </> toggle button to switch between modes (visible on hover or when active)
    • Displays raw default value as placeholder and shows default in help text when not overridden
    • Made label optional to support label-less sliders
  • BordersPanel (configurator/src/components/panels/BordersPanel.svelte):

    • Updated BASE_RADII defaults (xs: 2→2, s: 4→4, m: 6→8, l: 10→12, xl: 14→16, 2xl: 20→24)
    • Updated RADIUS_FINE array with new defaults and added rawDefault field with calc expressions (e.g., calc(8px * var(--sf-radius-scale)))
    • Updated COMPONENT_TOKENS with new token names (--sf-button-radius → --sf-btn-radius, etc.), adjusted defaults, and added rawDefault references to spacing/radius variables
    • Enhanced getRadiusValue() and getComponentVal() to detect and skip CSS expressions, returning defaults instead
    • Added raw CSS props to all SliderRow instances for divider width/gap and radius/padding tokens
  • LayoutPanel (configurator/src/components/panels/LayoutPanel.svelte):

    • Added raw CSS props to SliderRow instances for center max/gutter, sticky offsets, imposter margin, content/breakout widths, and alternate inner gap
    • Raw defaults reference semantic tokens (--sf-container-default, --sf-space-m, etc.)
  • SpacingPanel (configurator/src/components/panels/SpacingPanel.svelte):

    • Added raw CSS props to SliderRow instances for gap, content gap, and gutter
    • Raw defaults reference spacing tokens (--sf-space-m, --sf-space-s, --sf-space-l)

Implementation Details

  • Raw mode auto-activates when an override value matches CSS expression patterns (regex: /^(var|calc|clamp|min|max|env)\(/)
  • Empty raw input triggers reset to default
  • Placeholder shows the raw default value for reference
  • Toggle button styling: indigo when active, slate with hover effect when inactive
  • Maintains backward compatibility—raw props are optional; sliders work as before if not provided

https://claude.ai/code/session_011GgFN1mxJWjgGdwV6WGj2B

- SliderRow now accepts rawDefault + onRawSet props; shows actual
  framework default (e.g. "default: var(--sf-space-m)") below the
  slider and exposes a </> toggle to switch to a free-text raw
  input that accepts any CSS value including var()/calc() expressions.
  Auto-switches to raw mode when the current override is a CSS function.

- SpacingPanel: --sf-gap, --sf-content-gap, --sf-gutter now show their
  real defaults (var(--sf-space-m/s/l)) instead of hardcoded rem values.

- LayoutPanel: --sf-center-gutter, --sf-imposter-margin,
  --sf-alternate-inner-gap, --sf-sticky-offset-mobile/desktop,
  --sf-center-max, --sf-content-width, --sf-breakout-width all wired
  with rawDefault so their CSS var relationships are visible.

- BordersPanel: fix radius step defaults (m: 6→8px, l: 10→12px,
  xl: 14→16px, 2xl: 20→24px to match calc(Npx * var(--sf-radius-scale)));
  fix button token names --sf-button-* → --sf-btn-* to match the
  framework's optional/tokens.components.css; add rawDefault for all
  component shape, divider-gap, and divider-width tokens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GgFN1mxJWjgGdwV6WGj2B
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

SliderRow: add raw CSS input mode for token overrides
✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

Description

• Add a toggleable raw CSS input mode to SliderRow for var()/calc()/clamp() overrides.
• Wire raw defaults + raw overrides through Spacing/Layout/Borders panels for accurate token
 defaults.
• Fix border radius/component token defaults and rename button tokens to --sf-btn-*.
Diagram

graph TD
A["Panels (Spacing/Layout/Borders)"] --> B["SliderRow"] --> C{"showRaw?"}
C -- "yes" --> D["Raw CSS input"] --> F["Set/Reset handlers"] --> G["Overrides map"]
C -- "no" --> E["RangeWithNumber"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Always show both inputs (slider + raw field)
  • ➕ Discoverable without hover-only toggle
  • ➕ Avoids mode state and auto-detection edge cases
  • ➖ More visual noise in dense panels
  • ➖ Higher chance of conflicting edits / unclear source of truth
2. Extract a dedicated RawCssValueRow wrapper component
  • ➕ Keeps SliderRow focused on numeric use-cases
  • ➕ Panels opt-in explicitly, avoiding implicit coupling between numeric and raw modes
  • ➖ More components/props to maintain
  • ➖ Requires migrating existing SliderRow usage patterns
3. Use a CSS value parser for detection instead of regex
  • ➕ More robust detection of non-numeric values (e.g., attr(), color-mix(), custom functions)
  • ➕ Can cleanly distinguish 'numeric + unit' vs expression
  • ➖ Adds dependency/complexity for a small UX feature
  • ➖ May still need heuristics for partial/invalid input during typing

Recommendation: The PR’s approach (opt-in raw props + toggle + lightweight regex auto-detection) is a good balance for configurator UX: it preserves the slider-first workflow while enabling advanced CSS expressions when needed. Consider expanding the detection regex (or numeric detection) over time if more CSS functions appear in overrides, but avoid introducing a heavy parser unless expression variety grows significantly.

Files changed (5) +127 / -28

Enhancement (4) +126 / -27
SliderRow.svelteAdd raw CSS override mode with toggle and auto-detection +62/-10

Add raw CSS override mode with toggle and auto-detection

• Extends SliderRow with optional raw CSS props (rawDefault/currentRaw/onRawSet) and a toggleable raw input mode. Auto-detects CSS function overrides (var/calc/clamp/min/max/env), resets on empty raw input, and shows the raw default as help text when not overridden; label is now optional.

configurator/src/components/inputs/SliderRow.svelte

BordersPanel.svelteFix radius/component token defaults and add raw defaults to sliders +31/-17

Fix radius/component token defaults and add raw defaults to sliders

• Updates radius step defaults to match scaled calc() expressions and adds rawDefault fields for fine radius tokens. Renames button component tokens to --sf-btn-* and adds rawDefault/currentRaw/onRawSet wiring for divider and component token SliderRows; numeric parsing now ignores CSS expressions and falls back to defaults.

configurator/src/components/panels/BordersPanel.svelte

LayoutPanel.svelteWire layout token sliders for raw CSS defaults/overrides +24/-0

Wire layout token sliders for raw CSS defaults/overrides

• Adds rawDefault/currentRaw/onRawSet to multiple layout-related SliderRows (center widths/gutter, sticky offsets, imposter margin, content/breakout widths, alternate inner gap) so semantic defaults and var-based overrides are supported.

configurator/src/components/panels/LayoutPanel.svelte

SpacingPanel.svelteExpose spacing token defaults as raw CSS (var(--sf-space-*)) +9/-0

Expose spacing token defaults as raw CSS (var(--sf-space-*))

• Adds raw CSS props to gap/content-gap/gutter SliderRows so defaults are expressed via spacing tokens and users can override with CSS expressions without losing the slider workflow.

configurator/src/components/panels/SpacingPanel.svelte

Other (1) +1 / -1
badge-optimal.jsonUpdate optimal bundle-size badge value +1/-1

Update optimal bundle-size badge value

• Updates the reported gzip size in the "optimal" badge message from 17.6 kB to 37.0 kB.

badges/badge-optimal.json

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jackgranatowski, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d27b563-9342-4383-a59a-1e11184f883a

📥 Commits

Reviewing files that changed from the base of the PR and between 2a3ff37 and c5a1fd0.

📒 Files selected for processing (5)
  • badges/badge-optimal.json
  • configurator/src/components/inputs/SliderRow.svelte
  • configurator/src/components/panels/BordersPanel.svelte
  • configurator/src/components/panels/LayoutPanel.svelte
  • configurator/src/components/panels/SpacingPanel.svelte
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/design-token-vars-defaults-9896i6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

qodo-code-review Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 6 rules

Grey Divider


Action required

1. Raw input clears on typing ✓ Resolved 🐞 Bug ≡ Correctness
Description
In SliderRow, the raw text input’s value is set to an empty string whenever isRawOverride is
false, so normal typing triggers onRawSet(...) but then immediately re-renders the input back to
''. This makes raw mode effectively unusable for incremental typing (and for any raw value not
matching the regex), breaking the PR’s main feature across all panels that pass
rawDefault/onRawSet.
Code

configurator/src/components/inputs/SliderRow.svelte[R61-65]

+  {#if showRaw && rawDefault}
+    <input
+      type="text"
+      value={isRawOverride ? (currentRaw ?? '') : ''}
+      placeholder={rawDefault}
Relevance

⭐⭐⭐ High

Team consistently accepts Svelte input correctness/UX fixes (ColorInput/ClampField) in PRs 440 and
429.

PR-#440
PR-#429

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The component decides whether to show raw mode via showRaw, but then hard-resets the input value
to '' unless currentRaw already matches the CSS-function regex. Because oninput calls
onRawSet(v) on every keystroke, this creates a re-render loop that clears the field during normal
typing.

configurator/src/components/inputs/SliderRow.svelte[23-30]
configurator/src/components/inputs/SliderRow.svelte[61-75]
configurator/src/components/panels/LayoutPanel.svelte[165-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SliderRow` renders a raw `<input>` when `showRaw` is true, but it controls the input with `value={isRawOverride ? (currentRaw ?? '') : ''}`. When the user types, `onRawSet(v)` updates the parent override (`currentRaw`), but until the full string matches the regex, `isRawOverride` remains false and the component re-renders the input value back to empty, preventing incremental typing.

### Issue Context
Raw mode is explicitly enabled via `userRawMode`, so the input should display and preserve what the user is typing regardless of whether it matches the auto-detection regex.

### Fix Focus Areas
- configurator/src/components/inputs/SliderRow.svelte[23-75]

### Implementation notes
- Introduce a local draft state for the text field (e.g., `let rawDraft = $state(currentRaw ?? '')`).
- Keep `rawDraft` in sync with external changes to `currentRaw` (e.g., via an effect) when not actively editing.
- Render the input with `bind:value={rawDraft}` (or `value={rawDraft}`) so typing is never overwritten by the `isRawOverride` gate.
- Use `isRawOverride` only to decide whether to auto-enter raw mode (and/or to decide initial draft value), not to forcibly blank the input.
- Consider firing `onReset()` / `onRawSet()` on `onblur` (or debounce) to avoid resetting overrides mid-edit, but the core requirement is: the input must not be forced back to '' while typing.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Label typing mismatches template ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
SliderRow conditionally renders the label ({#if label}) but still types label as a required
string, so callers can’t omit it without TypeScript errors. This contradicts the intended support
for label-less sliders and forces call sites to pass label="".
Code

configurator/src/components/inputs/SliderRow.svelte[R4-10]

+  let {
+    label, help, value, min, max, step, unit, overridden, onChange, onReset,
+    rawDefault, currentRaw, onRawSet
+  }: {
    label: string;
    help?: string;
    value: number;
Relevance

⭐⭐ Medium

No prior reviews on optional prop typing; team accepts many small input-component fixes (PRs 440,
429).

PR-#440
PR-#429

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The template explicitly supports a missing/empty label, but the $props() typing still requires it.
LayoutPanel demonstrates current workarounds by passing an empty string label.

configurator/src/components/inputs/SliderRow.svelte[4-10]
configurator/src/components/inputs/SliderRow.svelte[35-39]
configurator/src/components/panels/LayoutPanel.svelte[93-126]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SliderRow` now supports rendering without a label at runtime, but the prop typing still requires `label: string`, preventing callers from omitting the prop and causing unnecessary `label=""` workarounds.

### Issue Context
Multiple call sites already pass `label=""` to achieve a label-less layout; the component API should allow `label` to be omitted.

### Fix Focus Areas
- configurator/src/components/inputs/SliderRow.svelte[4-16]
- configurator/src/components/panels/LayoutPanel.svelte[93-126]

### Implementation notes
- Change the prop type to `label?: string`.
- Optionally update call sites that pass `label=""` to omit `label` entirely for clarity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread configurator/src/components/inputs/SliderRow.svelte
Introduced a local rawDraft state and isEditing flag so the text field
keeps its value during incremental typing. External currentRaw changes
sync back to rawDraft only when the field is not focused, preventing
Svelte re-renders from blanking the input mid-edit.

Also loosened the label prop type to optional (label?: string) to match
the existing template guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GgFN1mxJWjgGdwV6WGj2B
@jackgranatowski
jackgranatowski merged commit ba2278f into main Jun 29, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants