huntabyte/shadcn-svelte
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 29, 2026
Low
Design risk in this codebase.
More findings
Verdict
Solid bones, sloppy joinery: a well-structured two-pane viewer undercut by copy-pasted selectors and indentation math nobody generalized. The biggest risk is accessibility debt hiding behind a UI that looks polished at a glance but fails screen readers and keyboard users at every icon-only control.
Files Rams reviewed
docs/src/lib/components/block-viewer-code.svelte
docs/src/lib/components/block-viewer-toolbar.svelte
docs/src/lib/components/block-viewer-tree.svelte
docs/src/lib/components/block-viewer-view-mobile.svelte
docs/src/lib/components/block-viewer-view.svelte
docs/src/lib/components/block-viewer.svelte
docs/src/lib/components/blocks-nav.svelte
docs/src/lib/components/cards/activity-goal.svelte
docs/src/lib/components/cards/appearance-settings.svelte
docs/src/lib/components/cards/cards-demo.svelte
docs/src/lib/components/cards/chat.svelte
docs/src/lib/components/cards/create-account.svelte
docs/src/lib/components/cards/demo.svelte
docs/src/lib/components/cards/exercise-minutes.svelte
docs/src/lib/components/cards/field-hear.svelte
docs/src/lib/components/cards/forms.svelte
docs/src/lib/components/cards/input-demo.svelte
docs/src/lib/components/cards/input-group-button-demo.svelte
docs/src/lib/components/cards/item-demo.svelte
docs/src/lib/components/cards/notion-prompt-form.svelte
docs/src/lib/components/cards/payments-actions-cell.svelte
docs/src/lib/components/cards/payments.svelte
docs/src/lib/components/cards/report-issue.svelte
docs/src/lib/components/cards/root-components.svelte
docs/src/lib/components/cards/share.svelte
docs/src/lib/components/cards/stats.svelte
docs/src/lib/components/cards/team-members.svelte
docs/src/lib/components/chart-code-viewer.svelte
docs/src/lib/components/chart-copy-button.svelte
docs/src/lib/components/chart-toolbar.svelte
Accessibility
docs/src/lib/components/block-viewer-toolbar.svelte:58
Desktop, Tablet, Mobile toggle buttons rely on title-only labeling
The `ToggleGroup.Item` controls for "Desktop", "Tablet", and "Mobile" each render only an icon (`MonitorIcon`, `TabletIcon`, `SmartphoneIcon`) with a `title` attribute for a name. Two buttons in the same toolbar, "Open in New Tab" and "Refresh Preview", pair their `title` with a `<span class="sr-only">`, but these three resize controls don't.
Why it matters
`title` only surfaces as a mouse hover tooltip and isn't reliably exposed as an accessible name by screen readers, so keyboard and assistive-tech users can't tell these three buttons apart or know what resizing action each performs.
Fix
Add a visually hidden sr-only span with the same text as the title to every icon-only toggle item, matching the pattern already used on adjacent buttons in this file.
<ToggleGroup.Item value="100" title="Desktop">
<MonitorIcon />
</ToggleGroup.Item><ToggleGroup.Item value="100" title="Desktop">
<MonitorIcon />
<span class="sr-only">Desktop</span>
</ToggleGroup.Item>docs/src/lib/components/block-viewer-view-mobile.svelte:24
Preview image alt text reads a file slug instead of a description
Both `<img>` elements in the mobile preview use `alt={ctx.item.name}`, so the accessible name is a slug like "sidebar-01" rather than a description of what the screenshot shows. The visible description text, `{ctx.item.description}`, is rendered right above the image but never reaches the alt attribute.
Why it matters
A screen reader user hears an opaque identifier instead of what the block preview actually depicts, so they get no equivalent of the sighted preview experience.
Fix
Use the existing human-readable description for the image's alt text instead of the internal slug.
<img
src="/img/registry/{ctx.item.name}-light.png"
alt={ctx.item.name}
data-block={ctx.item.name}
width={1440}
height={900}
class="object-cover dark:hidden"
/><img
src="/img/registry/{ctx.item.name}-light.png"
alt={ctx.item.description ?? ctx.item.name}
data-block={ctx.item.name}
width={1440}
height={900}
class="object-cover dark:hidden"
/>Spacing
docs/src/lib/components/block-viewer-tree.svelte:34
File and folder rows use different indent formulas at the same depth
The folder `Collapsible.Trigger` computes indent as `index * (index === 1 ? 1 : 1.2)}rem`, while the sibling file `Sidebar.MenuButton` two lines up uses `index * (index === 2 ? 1.2 : 1.3)}rem`. Two different formulas for the same `index` value produce different pixel offsets for files versus folders at identical tree depth.
Why it matters
A file and a folder at the same nesting level no longer align vertically, so the tree's hierarchy is misleading to scan even though the data structure says they're siblings.
Fix
Use one shared indent formula for both file and folder rows so siblings at the same depth align.
style="--index: {index * (index === 1 ? 1 : 1.2)}rem"style="--index: {index * 1.25}rem"docs/src/lib/components/block-viewer-tree.svelte:19
File row indentation uses hand-tuned magic multipliers instead of a scale
`Sidebar.MenuButton`'s indent is computed as `index * (index === 2 ? 1.2 : 1.3)}rem`, a one-off conditional multiplier rather than a fixed step from a spacing scale (e.g. `index * 1rem`).
Why it matters
Indentation that depends on special-cased index values instead of a consistent step drifts unpredictably as the tree grows deeper, and the next contributor adding a fourth or fifth nesting level has no rule to follow.
Fix
Replace the conditional multiplier with a single fixed step per depth level.
style="--index: {index * (index === 2 ? 1.2 : 1.3)}rem"style="--index: {index * 1.25}rem"Get this score on every PR.
Rams reviews each pull request on your repo and posts inline one-click fixes — about a minute per review.
Install Rams freeUX
docs/src/lib/components/block-viewer-toolbar.svelte:100
Copy command icon hard-swaps with no transition or timeout feedback
The copy button toggles between `<TerminalIcon />` and `<CheckIcon />` based on `clipboard.copied` with a plain `{#if}`, no CSS transition and no visible confirmation duration logic in this snippet.
Why it matters
An instant icon swap with no easing reads as a flicker rather than confirmation, so a user who clicks "copy" and looks away in the same instant can miss the only feedback that the copy succeeded.
Fix
Animate the icon swap with a short opacity or scale transition so the state change is perceivable as feedback, not a glitch.
{#if clipboard.copied}
<CheckIcon />
{:else}
<TerminalIcon />
{/if}{#if clipboard.copied}
<CheckIcon class="transition-all duration-150" />
{:else}
<TerminalIcon class="transition-all duration-150" />
{/if}Craft
docs/src/lib/components/block-viewer-tree.svelte:40
Copy-pasted active-state selector never fires on the folder trigger
`Collapsible.Trigger` carries `data-[active=true]:bg-muted-foreground/15` copied from the sibling `Sidebar.MenuButton` above it, but nothing in this component ever sets a `data-active` attribute on the trigger, only `isActive` is passed on the file's `MenuButton`. The rule is unreachable dead code.
Why it matters
Dead selectors like this accumulate across the codebase and mislead the next person editing this file into believing active-folder highlighting exists when it doesn't, costing debugging time later.
Fix
Remove selectors that reference state the component never sets, or wire the state through if the highlight is actually wanted.
<Collapsible.Trigger
style="--index: {index * (index === 1 ? 1 : 1.2)}rem"
class="hover:bg-muted-foreground/15 focus:bg-muted-foreground/15 focus-visible:bg-muted-foreground/15 active:bg-muted-foreground/15 data-[active=true]:bg-muted-foreground/15 rounded-none ps-(--index) whitespace-nowrap"
><Collapsible.Trigger
style="--index: {index * (index === 1 ? 1 : 1.2)}rem"
class="hover:bg-muted-foreground/15 focus:bg-muted-foreground/15 focus-visible:bg-muted-foreground/15 active:bg-muted-foreground/15 rounded-none ps-(--index) whitespace-nowrap"
>Typography
Color
Components
Motion
Working well
- The 'Open in New Tab' and 'Refresh Preview' buttons pair a title attribute with an sr-only span, giving both sighted and screen-reader users the accessible name they need. That's the right pattern for icon-only controls and it's a shame the resize toggles a few lines away don't follow it.
- Using ChevronRightIcon class="invisible" as a spacer on leaf file rows keeps file and folder rows optically aligned without adding conditional layout logic, a simple trick that avoids a second code path just for spacing.
- The file tree and code panel split into a clean two-column layout with a dedicated figcaption naming the active file, which gives sighted users clear orientation about what they're looking at without extra chrome.
Scored July 29, 2026 with Rams Engine v0.0.3 · Engine changelog
This page is an automated design review of huntabyte/shadcn-svelte’s UI code: 30 files read against 258 versioned rules covering accessibility, color, typography, spacing, components, UX, motion, and craft. The score is out of 100; any confirmed critical issue caps it at 59.
More design scores
Score your own repo.
Free on public repos, no account. The same engine that scored this page reads your UI code and mints a score page like this one.
Public repos only. The full engine reviews the UI code and mints a public score page — we email you the link too. Already-scored repos open instantly.