knecht-works on GitHub

knecht-works/knecht-cloud

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

30 files reviewed·November 10, 2026

View on GitHub

Elevated

Design risk in this codebase.

6issues
Critical, Serious & Moderate · top 5 shown below

Top fix

Add prefers-reduced-motion overrides to every infinite animation site.

See the fix

Verdict

Strong component thinking collides with a systematic gap: interactive state lives almost exclusively in hover and animation surfaces that keyboard and vestibular users cannot reach. The infinite animation with no reduced-motion escape is the single highest-risk pattern, present across files with no override anywhere.

Files Rams reviewed

app/pages/join/[token].vue

app/pages/login.vue

app/pages/projects/[id]/index.vue

app/pages/projects/[id]/settings.vue

app/pages/projects/[id]/settings/advanced.vue

app/pages/projects/[id]/settings/agent.vue

app/pages/projects/[id]/settings/integrations.vue

app/pages/projects/index.vue

app/pages/reset/[token].vue

app/pages/sessions/index.vue

app/pages/settings/advanced.vue

app/pages/settings/agent.vue

app/pages/settings/index.vue

app/pages/setup.vue

app/pages/system.vue

app/pages/workflows/index.vue

app/components/KAppSearch.vue

app/components/KAuthForm.vue

app/components/KChatTranscript.vue

app/components/KEnvLifecycle.vue

app/components/KIntegrationConnection.vue

app/components/KLogo.vue

app/components/KPageHeader.vue

app/components/KPanel.vue

app/components/KPasswordModal.vue

app/components/KPreviewBrowser.vue

app/components/KProfilePanel.vue

app/components/KProjectCard.vue

app/components/KProjectHeader.vue

app/components/KProjectHero.vue

95/100

Motion

1 critical1 serious
MotionCritical

app/pages/settings/index.vue:273

Infinite animation runs with no reduced-motion escape anywhere in these files

`animate-spin` loops forever, and no prefers-reduced-motion guard exists in any file of this review. For vestibular-sensitive users this is not a preference, it is a barrier.

Why it matters

Motion that cannot be turned off turns some visitors away at the door.

animate-spin
@media (prefers-reduced-motion: reduce) { animation: none; }
MotionSerious

app/pages/projects/[id]/index.vue:194

Infinite spinner ignores prefers-reduced-motion, harms vestibular users

The diagnosis-pending `UIcon` at line 194 of `projects/[id]/index.vue` carries `class="animate-spin"` with no `prefers-reduced-motion` guard in this file or in a global Tailwind config override visible here. The spinner runs indefinitely regardless of OS motion preference.

Why it matters

Continuous rotation causes nausea and disorientation for vestibular disorder users who have set the OS reduced-motion flag.

Fix

Guard infinite animations with motion:animate-none or a CSS media query so they stop when prefers-reduced-motion is set.

class="mr-1 inline-block size-3 animate-spin align-[-1px] text-dimmed"
class="mr-1 inline-block size-3 animate-spin motion-reduce:animate-none align-[-1px] text-dimmed"
96/100

UX

2 serious
UXSerious

app/pages/projects/[id]/settings/advanced.vue:24

Success toast verb "disconnected" breaks the single-verb flow set by the UI

In `advanced.vue` line 24, `toast.add` fires with `title: 'Project disconnected'`. The trigger button uses `label="Disconnect"`, the row uses `label="Disconnect project"`, and the modal title uses `"Disconnect project"`. Four surfaces, three different phrasings of the same verb.

Why it matters

Inconsistent verb forms across trigger, confirm, and feedback make the action harder to recognize in support logs and user recall.

Fix

Use one noun-qualified verb phrase across the trigger button, confirm modal, and success toast.

toast.add({ title: 'Project disconnected', description: 'Its environments are being removed in the background.', color: 'success' })
toast.add({ title: 'Project disconnected', description: 'Its environments are being removed in the background.', color: 'success' })
UXSerious

app/pages/login.vue:45

Forgot-password hint is a dead end with no actionable next step

In `login.vue` at line 45-47, the paragraph "Forgot your password? Ask an owner for a reset link." is styled `text-xs text-dimmed` with no link, button, or contact path. The only thing users can do after reading it is close the tab.

Why it matters

Users locked out of their account have no self-serve path, compounding frustration and generating owner support load.

Fix

Replace static hint text with an actionable link or mailto so locked-out users have a concrete next step.

<p class="text-xs text-dimmed">
        Forgot your password? Ask an owner for a reset link.
      </p>
<p class="text-xs text-dimmed">
        Forgot your password?
        <a href="mailto:" class="underline underline-offset-2 hover:text-toned">Ask an owner for a reset link.</a>
      </p>

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

Accessibility

1 serious
AccessibilitySerious

app/pages/projects/[id]/settings/agent.vue:115

Integration chip tooltip is keyboard-inaccessible; account name never surfaces

In `agent.vue`, `UTooltip` wraps a `<span>` chip for each `configuredIntegrations` entry. The `<span>` is not focusable, so keyboard users have no path to trigger the tooltip. The `mentionHint(i)` text: the only place the account handle appears: is therefore unreachable without a pointer device.

Why it matters

Keyboard users cannot discover which account each integration chip represents, breaking the Mentions configuration flow.

Fix

Replace the non-focusable span trigger with a button or add tabindex="0" and a keyboard handler so the hint is reachable.

<span class="k-mono flex items-center gap-1.5 rounded-full border border-default px-2.5 py-1 text-2xs text-muted">
<span
  tabindex="0"
  class="k-mono flex cursor-default items-center gap-1.5 rounded-full border border-default px-2.5 py-1 text-2xs text-muted"
>

Typography

No issues found

Color

No issues found

Spacing

No issues found

Components

No issues found

Craft

No issues found

Working well

  • The `removeProject` handler in `advanced.vue` wraps the DELETE call in `try/finally`, so `removing.value` resets whether the request succeeds or throws. This prevents a permanently-disabled "Disconnect" button on network failure: a subtle but important recovery path that most implementations skip.
  • The status card in `projects/[id]/index.vue` derives both its border color and gradient background from the same `statusColor` variable via `color-mix()`. One source drives two surfaces with zero duplication: this is exactly how to keep multi-surface state color in sync.

Scored November 10, 2026 with Rams Engine v0.0.4 · Engine changelog

This page is an automated design review of knecht-works/knecht-cloud’s UI code: 30 files read against 365 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