Rams MCP · The full engine, now in your coding agent
huntabyte on GitHub

huntabyte/shadcn-svelte

Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.

30 files reviewed·July 29, 2026

View on GitHub

Low

Design risk in this codebase.

8issues
Serious & Moderate · top 6 shown below

Top fix

Add sr-only labels to Desktop, Tablet, Mobile toggle buttons

See the fix

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

96/100

Accessibility

2 serious
AccessibilitySerious

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>
AccessibilitySerious

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"
/>
96/100

Spacing

2 serious
SpacingSerious

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"
SpacingSerious

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 free
98/100

UX

1 serious
UXSerious

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}
98/100

Craft

1 serious
CraftSerious

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

No issues found

Color

No issues found

Components

No issues found

Motion

No issues found

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.

Or get a design review on every pull requestInstall Rams