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

hcengineering/platform

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

30 files reviewed·July 25, 2026

View on GitHub

Elevated

Design risk in this codebase.

4issues
Critical & Serious

Top fix

Add a persistent or tap-triggered way to restore video controls

See the fix

Verdict

A working control shell hides a broken interaction underneath: touch users can lose video controls entirely with no way back. The Kanban row proves this team can do centralized, layered state well, but that discipline hasn't reached the accessibility-critical paths yet.

Files Rams reviewed

packages/hls/src/components/HlsVideo.svelte

packages/kanban/src/components/Kanban.svelte

packages/kanban/src/components/KanbanRow.svelte

packages/panel/src/components/Panel.svelte

packages/presentation/src/components/ActionContext.svelte

packages/presentation/src/components/AttributeBarEditor.svelte

packages/presentation/src/components/AttributeEditor.svelte

packages/presentation/src/components/AttributesBar.svelte

packages/presentation/src/components/Card.svelte

packages/presentation/src/components/DocPopup.svelte

packages/presentation/src/components/DownloadFileButton.svelte

packages/presentation/src/components/DrawingBoard.svelte

packages/presentation/src/components/DrawingBoardColorSelectorIcon.svelte

packages/presentation/src/components/DrawingBoardToolbar.svelte

packages/presentation/src/components/DrawingBoardToolbarColorIcon.svelte

packages/presentation/src/components/FilePreview.svelte

packages/presentation/src/components/FilePreviewPopup.svelte

packages/presentation/src/components/FileTypeIcon.svelte

packages/presentation/src/components/HTMLViewer.svelte

packages/presentation/src/components/IconWithEmoji.svelte

packages/presentation/src/components/Image.svelte

packages/presentation/src/components/InlineAttributeBar.svelte

packages/presentation/src/components/InlineAttributeBarEditor.svelte

packages/presentation/src/components/LiteMessageViewer.svelte

packages/presentation/src/components/MessageBox.svelte

packages/presentation/src/components/MessageViewer.svelte

packages/presentation/src/components/NavLink.svelte

packages/presentation/src/components/ObjectPopup.svelte

packages/presentation/src/components/ObjectSearchPopup.svelte

packages/presentation/src/components/PDFViewer.svelte

93/100

Accessibility

1 critical2 serious
AccessibilityCritical

packages/hls/src/components/HlsVideo.svelte:199

Video controls vanish on touch devices with no way to bring them back

The Plyr control bar in HlsVideo.svelte is hidden with `:global(.plyr.plyr--stopped:not(:hover) .plyr__controls)`, setting `opacity: 0`, `pointer-events: none`, and `transform: translateY(100%)`. This rule has no `@media (hover: hover)` guard, so it applies unconditionally on touch devices where `:hover` never persists after a tap. A touch user loads a stopped video, taps it once, the controls never render because pointer-events stays off, and there's no remaining path to press play.

Why it matters

Touch and mobile users, a large share of video traffic, cannot start playback at all: the play button is invisible and unclickable at the same time.

Fix

Scope the hover-hide behavior to hover-capable pointers only, and keep controls visible by default on touch.

// Hide controls when video is stopped and not hovered
:global(.plyr.plyr--stopped:not(:hover) .plyr__controls) {
  opacity: 0;
  pointer-events: none;
  transform: translateY(100%);
}
// Hide controls when video is stopped and not hovered (hover-capable pointers only)
@media (hover: hover) {
  :global(.plyr.plyr--stopped:not(:hover) .plyr__controls) {
    opacity: 0;
    pointer-events: none;
    transform: translateY(100%);
  }
}
AccessibilitySerious

packages/hls/src/components/HlsVideo.svelte:129

Captions are hard-disabled with no track element to enable

HlsVideo.svelte sets `options.captions = { active: false }` when constructing the Plyr player, and there is no `<track kind="captions">` anywhere in the component. Even if a viewer opens Plyr's settings menu looking for a captions toggle, there's nothing to turn on.

Why it matters

Deaf and hard-of-hearing viewers have no path to captions on this video at all, not even a hidden or off-by-default option.

Fix

Add a caption track element and enable Plyr's captions option so users can turn captions on when a track source exists.

options.captions = {
  active: false
}
options.captions = {
  active: false,
  update: true
}
// add a matching <track kind="captions" srclang="en" label="English" src={captionsSrc} /> inside the <video> element when a track is available
AccessibilitySerious

packages/hls/src/components/HlsVideo.svelte:171

Missing-caption lint warning suppressed instead of fixed on the video element

The `<video>` element in HlsVideo.svelte is preceded by `<!-- svelte-ignore a11y-media-has-caption -->`, which silences Svelte's built-in warning about missing captions rather than adding a `<track>` child to resolve it.

Why it matters

Suppressing the linter hides the gap from future contributors, so the missing-captions problem persists silently across every video that reuses this component.

Fix

Remove the ignore comment and add a real `<track>` element (or conditionally render one) so the linter passes on substance rather than suppression.

<!-- svelte-ignore a11y-media-has-caption -->
<video bind:this={video} width="100%" height="100%" preload={preload ? 'auto' : 'none'} disablepictureinpicture />
<video bind:this={video} width="100%" height="100%" preload={preload ? 'auto' : 'none'} disablepictureinpicture>
  {#if captionsSrc}
    <track kind="captions" srclang="en" label="English" src={captionsSrc} />
  {/if}
</video>
98/100

UX

1 serious
UXSerious

packages/presentation/src/components/AttributeEditor.svelte:62

Attribute editor has no error state when its dynamic component fails to load

In AttributeEditor.svelte, `{#await editor}...{:then instance}` renders the resolved editor component but defines no `{:catch}` branch. If the `getResource(editorMixin.inlineEditor)` promise rejects, the block stays on the pending placeholder `...` indefinitely.

Why it matters

A failed dynamic import (network blip, bad module path) leaves the field permanently stuck on a loading ellipsis with no retry or error message, and the user can't tell if the app is broken or just slow.

Fix

Add a `{:catch}` branch to the await block that surfaces a visible error state instead of an indefinite pending placeholder.

{#await editor}
  ...
{:then instance}
{#await editor}
  ...
{:then instance}
  <!-- existing component render -->
{:catch error}
  <span class="error">Failed to load editor</span>
{/await}

Typography

No issues found

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • The `.selection` and `.checked` states in KanbanRow.svelte are layered with distinct box-shadow and background treatments rather than color alone, so checked+selected cards stay visually distinguishable from either state individually.
  • AttributeEditor.svelte derives `isReadonly` as a reactive statement from `attribute.readonly` and `editable`, keeping the template's conditional logic out of the markup and easy to audit in one place.
  • The custom HLS loader rewrites blob URLs and injects the auth header per-request, keeping token handling centralized in one place rather than scattered across call sites.

Scored July 25, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 22, 2026: 59/100. This rescore on v0.0.3: 59/100.

This page is an automated design review of hcengineering/platform’s UI code: 30 files read against 291 versioned rules covering accessibility, color, typography, spacing, components, UX, motion, and craft. The score is out of 100; confirmed criticals cap it — one at 59, two at 49, three or more at 39.

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