lobehub on GitHub

lobehub/lobe-chat

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

30 files reviewed·August 1, 2026

View on GitHub

Top fix

Add a shape or label so KindDot doesn't rely on color alone

See the fix

Verdict

Solid componentization undercut by sloppy micro-details: jittering digits, clipped text with no recovery, and color-only status dots. The shared primitives are the credit here, but every numeric or truncated surface needs a second pass before this reads as considered.

Files Rams reviewed

src/components/server/MobileNavLayout.tsx

src/app/layout.tsx

packages/builtin-tool-lobe-agent/src/client/components/SortableTodoList/index.tsx

packages/shared-tool-ui/src/components/FileChangeStats/index.tsx

packages/shared-tool-ui/src/components/TodoProgress/index.tsx

src/components/404/index.tsx

src/components/Analytics/index.tsx

src/components/AsyncBoundary/index.tsx

src/components/AsyncError/index.tsx

src/components/AvatarUpload/index.tsx

src/components/BootErrorBoundary/index.tsx

src/components/BrandWatermark/index.tsx

src/components/Cell/index.tsx

src/components/CodeEditorPane/index.tsx

src/components/CollapsibleContent/index.tsx

src/components/CommentList/index.tsx

src/components/CopyableLabel/index.tsx

src/components/DataStyleModal/index.tsx

src/components/Descriptions/index.tsx

src/components/DotsLoading/index.tsx

src/components/DragUploadZone/index.tsx

src/components/Error/index.tsx

src/components/ErrorBoundary/index.tsx

src/components/FileIcon/index.tsx

src/components/FileParsingStatus/index.tsx

src/components/FormAction/index.tsx

src/components/GalleyGrid/index.tsx

src/components/GuideModal/index.tsx

src/components/HighlightNotification/index.tsx

src/components/ImageItem/index.tsx

94/100

Typography

3 serious
TypographySerious

packages/shared-tool-ui/src/components/TodoProgress/index.tsx:214

Todo count badge digits shift the header layout as todos complete

The header count badge `{completed}/{total}` in TodoPanelHeader has no tabular-nums applied. As todos complete and the numerator grows (e.g. 9/12 to 10/12), the digit count changes and the badge width shifts against the label text next to it.

Why it matters

Every completed todo nudges the badge and header label horizontally, so the progress indicator feels jumpy exactly when a user is watching it update in real time.

Fix

Apply tabular-nums to the count badge so its width stays fixed as digits change.

<span className={styles.headerCount}>
  {completed}/{total}
</span>
<span className={styles.headerCount} style={{ fontVariantNumeric: 'tabular-nums' }}>
  {completed}/{total}
</span>
TypographySerious

packages/shared-tool-ui/src/components/FileChangeStats/index.tsx:81

Line-stat digits without tabular-nums cause the pill to jitter on update

The `+{linesAdded}` and `-{linesDeleted}` spans in LineStats render plain proportional digits with no `font-variant-numeric: tabular-nums`. As a file's diff stat updates (e.g. +9 to +10), the digit width changes and the pill and anything next to it shifts horizontally.

Why it matters

Live-updating diff stats next to file names cause visible layout shift on every update, making the list feel unstable and harder to scan while a diff is streaming in.

Fix

Apply tabular-nums to numeric stat text so digit width stays fixed regardless of value.

{showAdded && <span className={styles.lineAdded}>+{linesAdded}</span>}
{showDeleted && <span className={styles.lineDeleted}>-{linesDeleted}</span>}
{showAdded && <span className={styles.lineAdded} style={{ fontVariantNumeric: 'tabular-nums' }}>+{linesAdded}</span>}
{showDeleted && <span className={styles.lineDeleted} style={{ fontVariantNumeric: 'tabular-nums' }}>-{linesDeleted}</span>}
TypographySerious

packages/shared-tool-ui/src/components/TodoProgress/index.tsx:225

Truncated step detail in the panel header has no way to recover the full text

The `headerDetail` span rendering `{detail}` in TodoPanelHeader is clamped with ellipsis truncation but carries no `title` attribute. A long step name (e.g. a file path or command) gets clipped with no way to see the rest.

Why it matters

When the step detail is longer than the header width, the user loses the truncated portion entirely, with no hover or tap affordance to recover what task is actually running.

Fix

Add a title attribute mirroring the truncated text so a hover reveals the full string.

<span className={styles.headerDetail}>{detail}</span>
<span className={styles.headerDetail} title={detail}>{detail}</span>
98/100

Accessibility

1 serious
AccessibilitySerious

packages/shared-tool-ui/src/components/FileChangeStats/index.tsx:61

Color-only dot leaves file change kind unreadable for colorblind users

KindDot renders a bare `<span className={cx(styles.kindDot, KIND_CLASS[kind], className)} />` with no text, title, or aria-label. Added, deleted, modified, and renamed are distinguished only by the CSS class color applied to an empty span, so the change kind has no non-color signal at all.

Why it matters

A colorblind user or anyone in a low-contrast environment sees an identical gray dot for every file regardless of whether it was added, deleted, modified, or renamed, so they can't tell which files changed how without opening each one.

Fix

Add a title attribute (or aria-label) on the dot that names the change kind so the information survives without color.

export const KindDot = memo<{ className?: string; kind: FileChangeKind }>(({ className, kind }) => (
  <span className={cx(styles.kindDot, KIND_CLASS[kind], className)} />
));
export const KindDot = memo<{ className?: string; kind: FileChangeKind }>(({ className, kind }) => (
  <span className={cx(styles.kindDot, KIND_CLASS[kind], className)} title={kind} aria-label={kind} />
));

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

UX

No issues found

Craft

No issues found

Working well

  • KindDot and LineStats are extracted once into a shared package instead of being hand-rolled per consumer, which is exactly the right move for repeated visual primitives like diff-stat pills.
  • The truncation trio on headerLabel (overflow hidden, white-space nowrap, text-overflow ellipsis) is correctly complete, so the ellipsis actually renders instead of silently failing to clip.
  • Using useUnmount to flush pending saves on teardown is a solid guard against data loss when the component leaves the tree mid-debounce.

Scored August 1, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 9, 2026: 59/100. This rescore on v0.0.3: 92/100.

This page is an automated design review of lobehub/lobe-chat’s UI code: 30 files read against 309 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