Skip to content

fix: default border token, dither-kit theme toggle + DialKit tweak panel - #72

Merged
ripgrim merged 5 commits into
mainfrom
dither-kit-theme
Jul 4, 2026
Merged

fix: default border token, dither-kit theme toggle + DialKit tweak panel#72
ripgrim merged 5 commits into
mainfrom
dither-kit-theme

Conversation

@ripgrim

@ripgrim ripgrim commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Three fixes for the deployed /dither-kit page:

  1. White borders — Tailwind v4 has no --default-border-color mechanism (that token was dead config), so bare border utilities fell back to currentColor → white hairlines on dark. A base-layer rule now points default borders at --color-border app-wide.

  2. Theme toggle restored — the app is dark-only, so the page scopes its own theme: a .dither-light class overrides the shadcn-alias tokens on the page wrapper, and the wrapper's dark class drives dark: variants. Persisted per visitor.

  3. DialKit tweak panel — the hand-rolled tweak sidebar is replaced with DialKit: floating panel with a bloom folder (preset select + custom blur/brightness/opacity/saturate sliders), per-series variant selects, stacking toggle, pie inner-radius + entrance-duration sliders, and a replay-all action. productionEnabled so it works for visitors. Code snippets still track every change.

Summary by CodeRabbit

  • New Features
    • Added a dedicated docs experience for DitherKit, including install snippets, chart gallery, and copyable code examples.
    • Enhanced chart customization with floating dial controls for bloom, variants, stacking, pie inner radius, and entrance timing.
    • Introduced a light/dark theme toggle with persisted preference on the docs page.
    • Added replay controls, including a “replay all” action to refresh animations across sections.
  • Bug Fixes / Improvements
    • Updated docs styling for consistent borders and a polished light-theme palette.

ripgrim added 2 commits July 3, 2026 17:57
- Tailwind v4 has no --default-border-color mechanism; the bare border
  utility fell back to currentColor (white on dark). Base layer rule now
  points default borders at the border token.
- /dither-kit gets its theme toggle back: page-scoped .dither-light token
  overrides + a dark wrapper class for dark: variants, persisted in
  localStorage. The app itself stays dark-only.
The hand-rolled sidebar (Field/Select/Range/TweakSidebar) is replaced by
DialKit's floating control panel (https://joshpuckett.me/dialkit) —
bloom folder (preset + custom sliders), per-series variant selects,
stacking toggle, pie radius + entrance sliders, and a replay-all action.
productionEnabled so visitors on tripwire.sh get the panel too; the
settle-replay behavior is unchanged (any tweak replays after 300ms).
@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tripwire Ready Ready Preview, Comment Jul 4, 2026 7:42am

Request Review

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

React Doctor found 1 issue in 1 file · 1 error · score 46 / 100 (Critical) · vs main

Errors

⚠️ Warning: .github/workflows/react-doctor.yml is configured incorrectly. See below to fix.

React Doctor compares against main to report only the issues this pull request introduces. This run couldn't complete that comparison (usually a shallow CI checkout with no merge base), so it listed every issue in the changed files, including ones that already existed on main.

Add fetch-depth: 0 to the actions/checkout step in .github/workflows/react-doctor.yml so the checkout includes the history React Doctor needs:

 jobs:
   react-doctor:
     steps:
       - uses: actions/checkout@v5
+        with:
+          fetch-depth: 0

       - uses: millionco/react-doctor@v2

To silence this warning, set silence-missing-baseline-warning: true on the React Doctor action.

Reviewed by React Doctor for commit 3c16dc4. See inline comments for fixes.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ripgrim, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b9dec522-5fe5-42c3-9903-5ca93913c126

📥 Commits

Reviewing files that changed from the base of the PR and between f91cd7b and 3c16dc4.

📒 Files selected for processing (1)
  • apps/web/biome.json
📝 Walkthrough

Walkthrough

This pull request adds DialKit to the dither-kit page, replaces the old sidebar-driven tweak UI with floating dial controls, and moves docs/chart rendering into new shared content, UI, and section modules. The route now derives tweak state from DialKit params, keeps replay counters, and uses a persisted page theme hook. Styles add a light theme token set and update border-color handling. Biome overrides were expanded for the new docs component files.

Sequence Diagram(s)

See the hidden review stack artifact.

Related PRs: None provided.

Suggested labels: enhancement, dependencies, refactor

Suggested reviewers: None provided.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: border token fix, dither-kit theme toggle, and DialKit tweak panel.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/web/src/routes/dither-kit.tsx (2)

1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move React/core imports to the top of the import block.

This new DialKit import block leaves the React import after external packages, contrary to the project import order.

Proposed import order
+import { Fragment, type ReactNode, useEffect, useRef, useState } from "react"
 import { createFileRoute } from "`@tanstack/react-router`"
 import { DialRoot, useDialKit } from "dialkit"
 import "dialkit/styles.css"
 import {
   CheckIcon,
   CopyIcon,
   MoonIcon,
   RefreshCcwIcon,
   SunIcon,
 } from "lucide-react"
-import { Fragment, type ReactNode, useEffect, useRef, useState } from "react"

As per coding guidelines, "**/*.{ts,tsx,js,jsx}: Import Order: React/core libraries, External libraries, UI components (#/components/ui/), Utilities (#/lib/), Feature imports, Package imports (@tripwire/*)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/dither-kit.tsx` around lines 1 - 11, The import block in
dither-kit should follow the project import order by moving the React/core
imports to the top before external packages like createFileRoute, dialkit, and
lucide-react. Update the existing import grouping around the React import so the
React symbols (Fragment, ReactNode, useEffect, useRef, useState) are listed
first, keeping the rest of the DialKit and icon imports after them.

Source: Coding guidelines


441-443: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trim the decorative comment.

This new comment is section-style, verbose, and contains an em dash; the useDialKit call is self-descriptive enough without it.

As per coding guidelines, "Comments should be sparse, contain no em dashes or overly verbose language patterns."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/dither-kit.tsx` around lines 441 - 443, The comment
around the useDialKit configuration is too verbose and decorative; remove or
trim it so the code remains self-descriptive without section-style prose. Update
the nearby explanatory text in dither-kit.tsx to be sparse and neutral, and
avoid em dashes or long descriptive phrasing while keeping any necessary context
minimal.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/routes/dither-kit.tsx`:
- Around line 524-535: The icon-only theme button in the dither-kit route uses a
generic aria-label that does not indicate the resulting theme state. Update the
button in the theme toggle UI (the element wired to toggleTheme and rendering
MoonIcon/SunIcon) to use an action-specific accessible label that reflects what
will be activated, such as the next theme mode rather than “Toggle theme,” so
assistive tech users can understand the action.
- Around line 169-184: The usePageTheme hook needs safer persistence and an
explicit type. Update usePageTheme to catch any localStorage access errors
around the initial getItem and the toggle’s setItem so the page still works when
storage is blocked, and return a typed shape for light and toggle. Also remove
the JSDoc from this internal hook since the guideline only allows JSDoc for
exported functions/types, and keep the fix localized to usePageTheme.
- Around line 471-491: The snippet generators are missing the DialKit-driven
animation duration, so copied chart code does not reflect the current
`entranceMs`/`tweaks.duration` setting. Update the chart snippet template
helpers in `dither-kit.tsx` to include `animationDuration` by appending the
existing `durationAttr(t)` alongside `stackAttr(t)` and `bloomAttr(t)`, so the
generated snippets match the preview configuration.

---

Nitpick comments:
In `@apps/web/src/routes/dither-kit.tsx`:
- Around line 1-11: The import block in dither-kit should follow the project
import order by moving the React/core imports to the top before external
packages like createFileRoute, dialkit, and lucide-react. Update the existing
import grouping around the React import so the React symbols (Fragment,
ReactNode, useEffect, useRef, useState) are listed first, keeping the rest of
the DialKit and icon imports after them.
- Around line 441-443: The comment around the useDialKit configuration is too
verbose and decorative; remove or trim it so the code remains self-descriptive
without section-style prose. Update the nearby explanatory text in
dither-kit.tsx to be sparse and neutral, and avoid em dashes or long descriptive
phrasing while keeping any necessary context minimal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f25fce3d-594a-4730-9269-a870f30a1f92

📥 Commits

Reviewing files that changed from the base of the PR and between 8d56e91 and 84b88fa.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • apps/web/package.json
  • apps/web/src/routes/dither-kit.tsx
  • apps/web/src/styles.css

Comment thread apps/web/src/routes/dither-kit.tsx Outdated
Comment on lines +471 to +491
entranceMs: [900, 300, 2400, 100],
replayAll: { type: "action" },
},
{
onAction: (path: string) => {
if (path === "replayAll") replayAll()
},
}
)

const tweaks: Tweaks = {
bloomPreset: params.bloom.preset as BloomPreset,
blur: params.bloom.blur,
brightness: params.bloom.brightness,
opacity: params.bloom.opacity,
saturate: params.bloom.saturate,
primaryVariant: params.desktopVariant as AreaVariant,
secondaryVariant: params.mobileVariant as AreaVariant,
stacked: params.stacked,
donutRadius: params.pieInnerRadius,
duration: params.entranceMs,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reflect entranceMs in the generated snippets.

The DialKit slider now drives tweaks.duration and the previews pass it as animationDuration, but the snippet helpers still omit animationDuration, so this control is not reflected in copied code.

Proposed direction
 const stackAttr = (t: Tweaks) => (t.stacked ? ' stackType="stacked"' : "")
+const durationAttr = (t: Tweaks) =>
+  t.duration === 900 ? "" : ` animationDuration={${t.duration}}`

Then append ${durationAttr(t)} in each chart snippet template alongside stackAttr(t)/bloomAttr(t).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/dither-kit.tsx` around lines 471 - 491, The snippet
generators are missing the DialKit-driven animation duration, so copied chart
code does not reflect the current `entranceMs`/`tweaks.duration` setting. Update
the chart snippet template helpers in `dither-kit.tsx` to include
`animationDuration` by appending the existing `durationAttr(t)` alongside
`stackAttr(t)` and `bloomAttr(t)`, so the generated snippets match the preview
configuration.

Comment thread apps/web/src/routes/dither-kit.tsx Outdated
Comment on lines +524 to +535
<button
type="button"
aria-label="Toggle theme"
onClick={toggleTheme}
className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
>
{light ? (
<MoonIcon className="size-4" />
) : (
<SunIcon className="size-4" />
)}
</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use an action-specific label for the icon-only theme button.

aria-label="Toggle theme" does not tell assistive tech users which theme will be activated.

Proposed fix
-              aria-label="Toggle theme"
+              aria-label={light ? "Switch to dark theme" : "Switch to light theme"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
type="button"
aria-label="Toggle theme"
onClick={toggleTheme}
className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
>
{light ? (
<MoonIcon className="size-4" />
) : (
<SunIcon className="size-4" />
)}
</button>
<button
type="button"
aria-label={light ? "Switch to dark theme" : "Switch to light theme"}
onClick={toggleTheme}
className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
>
{light ? (
<MoonIcon className="size-4" />
) : (
<SunIcon className="size-4" />
)}
</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/dither-kit.tsx` around lines 524 - 535, The icon-only
theme button in the dither-kit route uses a generic aria-label that does not
indicate the resulting theme state. Update the button in the theme toggle UI
(the element wired to toggleTheme and rendering MoonIcon/SunIcon) to use an
action-specific accessible label that reflects what will be activated, such as
the next theme mode rather than “Toggle theme,” so assistive tech users can
understand the action.

Entrance animation now fires only on page load or the replay buttons —
turning a dial adjusts the live chart without re-running the sweep.
Comment thread apps/web/src/routes/dither-kit.tsx Outdated

/* -------------------------------------------------------------- showcase */

function Showcase({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/no-multi-comp (warning)

This file declares several components, so each component is harder to find, test, and change.

Fix → Move secondary components into their own files so each component stays easier to find, test, and change.

Docs

@@ -658,10 +397,8 @@ function Showcase({
/* ------------------------------------------------------------------ page */

function DitherKitDocs() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/no-giant-component (warning)

Component "DitherKitDocs" is 427 lines long, which is hard to read & change. Split it into a few smaller components.

Fix → Pull each section into its own component so the parent is easier to read, test, and change.

Docs

@@ -658,10 +397,8 @@ function Showcase({
/* ------------------------------------------------------------------ page */

function DitherKitDocs() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/no-multi-comp (warning)

This file declares several components, so each component is harder to find, test, and change.

Fix → Move secondary components into their own files so each component stays easier to find, test, and change.

Docs

- usePageTheme hardened: localStorage reads/writes wrapped so disabled
  storage can't break the page, and the hook gets an explicit
  PageThemeState return type (review: stability)
- snippets now reflect the entrance slider — non-default entranceMs
  emits animationDuration={n} in every copied chart snippet (review:
  functional correctness)
- the theme button's label says which theme it switches to instead of
  a generic toggle (review: a11y)
- react-doctor's no-giant-component / no-multi-comp on the route: the
  893-line file is now a ~140-line route composing
  components/dither-kit-docs/{content,ui,sections} — data + snippet
  builders, page primitives (strip, copy, highlighter, pills,
  showcase), and the six page sections
@ripgrim

ripgrim commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all review comments:

  • Theme persistence hardenedusePageTheme wraps localStorage in try/catch (storage-disabled browsers keep a working in-memory toggle) and returns an explicit PageThemeState.
  • entranceMs reflected in snippets — non-default durations emit animationDuration={n} in every generated chart snippet via a durationAttr helper.
  • Action-specific theme labelaria-label now reads "Switch to light/dark theme".
  • react-doctor giant-component/multi-comp — the 893-line route is split into components/dither-kit-docs/ (content + snippet builders, UI primitives, page sections); the route itself is ~140 lines with a single component.

@ripgrim
ripgrim merged commit c4f0a4d into main Jul 4, 2026
10 of 11 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
apps/web/src/components/dither-kit-docs/sections.tsx (2)

63-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract named props interfaces instead of inline anonymous types.

HeroSection (and likewise InstallSection at Lines 154-160 and ChartGallery at Lines 254-266) declare their props as inline object types. Define PascalCase *Props interfaces.

♻️ Example for HeroSection
+interface HeroSectionProps {
+  light: boolean
+  onToggleTheme: () => void
+  tweaks: Tweaks
+  bloom: BloomInput
+  replayToken: number
+  onReplay: () => void
+}
+
-export function HeroSection({
-  light,
-  onToggleTheme,
-  tweaks,
-  bloom,
-  replayToken,
-  onReplay,
-}: {
-  light: boolean
-  onToggleTheme: () => void
-  tweaks: Tweaks
-  bloom: BloomInput
-  replayToken: number
-  onReplay: () => void
-}) {
+export function HeroSection({
+  light,
+  onToggleTheme,
+  tweaks,
+  bloom,
+  replayToken,
+  onReplay,
+}: HeroSectionProps) {

As per coding guidelines: "Always define props interface for components." and "Interfaces: PascalCase with suffix (WorkflowEditorProps)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/sections.tsx` around lines 63 - 77,
Extract the inline props object type from HeroSection into a named PascalCase
interface (for example, HeroSectionProps) and use it in the function signature;
apply the same pattern to InstallSection and ChartGallery so all component props
follow the project guideline of defining explicit props interfaces. Keep the
interface definitions close to their respective components in sections.tsx and
ensure the names are suffixed with Props for easy identification.

Source: Coding guidelines


51-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Decorative doc comment with an em dash violates comment guidelines.

This /** */ block is not attached to an exported function/type and also uses an em dash. Convert to a sparse plain comment (or drop it).

♻️ Proposed change
-/** The docs page, section by section — the route composes these. */
+// Section components composed by the /dither-kit route.

As per coding guidelines: "Use JSDoc for exported functions/types only. No ASCII divider comments or decorative multi-line doc comments." and "Comments should ... contain no em dashes."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/sections.tsx` at line 51, The comment
in sections.tsx is a decorative JSDoc block that is not documenting an exported
function/type and also contains an em dash. Remove it or replace it with a
simple plain single-line comment, and keep comments sparse and non-decorative;
use JSDoc only for exported symbols such as functions or types.

Source: Coding guidelines

apps/web/src/components/dither-kit-docs/ui.tsx (4)

16-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inline style object instead of Tailwind.

The gradient/mask values are static strings and can be expressed as Tailwind arbitrary-value classes instead of the style prop.

As per coding guidelines, "Use Tailwind only, no inline styles."

✏️ Suggested conversion
-      className={`text-border ${className}`}
-      style={{
-        backgroundImage:
-          "repeating-conic-gradient(currentColor 0% 25%, transparent 0% 50%)",
-        backgroundSize: "6px 6px",
-        maskImage:
-          "linear-gradient(to right, transparent, black 18%, black 82%, transparent)",
-      }}
+      className={`text-border bg-[repeating-conic-gradient(currentColor_0%_25%,transparent_0%_50%)] bg-[length:6px_6px] [mask-image:linear-gradient(to_right,transparent,black_18%,black_82%,transparent)] ${className}`}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 16 - 22, Replace
the inline style object in the dither-kit docs UI component with Tailwind
arbitrary-value classes, since the gradient and mask values are static. Update
the relevant JSX in the UI component to express the background image, background
size, and mask image using Tailwind-only className utilities instead of the
style prop, keeping the same visual effect.

Source: Coding guidelines


6-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment style deviates from repo conventions.

Several issues recur through the file:

  • ASCII section dividers at Lines 27, 69, 105, 194, 235.
  • A decorative, non-attached file-level doc comment at Lines 6-7.
  • JSDoc on a non-exported function (tokenize) at Lines 123-125.
  • Em dashes in comments at Lines 9-11, 34-35, 123-125, 151-153.

As per coding guidelines, "No ASCII divider comments or decorative multi-line doc comments" and "Comments should be sparse, contain no em dashes or overly verbose language patterns."

Also applies to: 9-11, 27-27, 34-35, 69-69, 105-105, 123-125, 151-153, 194-194, 235-235

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 6 - 7, The
comments in ui.tsx should be brought in line with repo style by removing the
decorative file-level doc comment, all ASCII divider comments, and the JSDoc on
the non-exported tokenize helper. Also rewrite the remaining comments to be
sparse and plain, eliminating em dashes and overly verbose phrasing while
keeping only any truly necessary inline notes near the relevant symbols such as
tokenize and the other component helpers.

Source: Coding guidelines


11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inline prop type literals instead of named props interfaces.

DitherStrip, CopyLine, Pill, ReplayButton, and Showcase all destructure against anonymous inline object types rather than named interfaces. Showcase (Lines 237-252) is the most impacted, with 6 fields typed inline.

As per coding guidelines, "Always define props interface for components" and "Never introduce a raw inline type to a component-level or route-level file."

✏️ Example fix for `Showcase`
+interface ShowcaseProps {
+  title: string
+  install: string
+  code: string
+  toolbar?: ReactNode
+  children: ReactNode
+  tall?: boolean
+}
+
-export function Showcase({
-  title,
-  install,
-  code,
-  toolbar,
-  children,
-  tall = false,
-}: {
-  title: string
-  install: string
-  code: string
-  /** Extra controls rendered in the card toolbar (e.g. replay). */
-  toolbar?: ReactNode
-  children: ReactNode
-  tall?: boolean
-}) {
+export function Showcase({
+  title,
+  install,
+  code,
+  toolbar,
+  children,
+  tall = false,
+}: ShowcaseProps) {

Also applies to: 82-82, 197-205, 222-222, 237-252

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/ui.tsx` at line 11, Replace the
anonymous inline prop type annotations in DitherStrip, CopyLine, Pill,
ReplayButton, and Showcase with named props interfaces defined in the same
module. Update each component signature to destructure from its interface type
instead of a raw inline object, and make Showcase’s multi-field props especially
use a dedicated interface to match the component props guideline.

Source: Coding guidelines


211-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Conditional classes built with raw ternaries instead of cn().

Pill's active-state classes and Showcase's tall-driven height classes use manual template-literal ternaries rather than the shared cn() helper.

As per coding guidelines, "Use cn() from @tripwire/ui/utils for conditional classes."

Also applies to: 292-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 211 - 215, The
conditional class logic in Pill and Showcase is using raw template-literal
ternaries instead of the shared cn() helper. Update the relevant className
construction in Pill and the tall-driven height handling in Showcase to use cn()
from `@tripwire/ui/utils` for conditional merging, keeping the same active/tall
behavior while moving the branching into cn()-based arguments.

Source: Coding guidelines

apps/web/src/components/dither-kit-docs/content.ts (1)

7-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment style deviates from repo conventions.

ASCII section dividers (Lines 13, 29, 64, 99) and JSDoc placed on non-exported helpers or as a decorative file-level summary (Lines 7-8, 18, 101, 112) both conflict with the stated conventions.

As per coding guidelines, "Do not use ASCII divider comments (// ─── Section Name ───────). Use whitespace and clear naming instead" and "Use JSDoc for exported functions/types only. No ASCII divider comments or decorative multi-line doc comments."

✏️ Example cleanup
-/* ------------------------------------------------------- package manager */
-
+
 export const PMS = ["npm", "pnpm", "yarn", "bun"] as const
 export type Pm = (typeof PMS)[number]

-/** The runner each package manager uses for one-off CLIs. */
+// The runner each package manager uses for one-off CLIs.
 const PM_RUNNER: Record<Pm, string> = {

Also applies to: 13-13, 18-18, 29-29, 64-64, 99-99, 101-101, 112-112

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/content.ts` around lines 7 - 8,
Remove the decorative file-level JSDoc summary and any JSDoc blocks attached to
non-exported helpers in content.ts, since only exported functions/types should
use JSDoc. Also replace the ASCII section divider comments with whitespace and
clearer naming, keeping the structure readable without decorative separators;
update the affected helpers and section markers consistently throughout the
file.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/components/dither-kit-docs/content.ts`:
- Around line 113-114: The entrance duration default is hardcoded in both the
docs snippet generator and the live route, which can cause them to drift.
Extract a shared DEFAULT_ENTRANCE_MS constant and use it in content.ts’s
durationAttr helper and in the dither-kit route’s default value so both
references stay synchronized.

In `@apps/web/src/components/dither-kit-docs/sections.tsx`:
- Around line 89-100: The theme toggle in sections.tsx is using a raw <button>
instead of the shared Button primitive. Replace the existing button inside the
theme toggle component with the exported Button component from the UI package,
preserving the same aria-label, onClick handler, and MoonIcon/SunIcon rendering
while applying the appropriate icon/ghost styling through Button props or
variant classes.

In `@apps/web/src/components/dither-kit-docs/ui.tsx`:
- Around line 71-79: The useCopy hook shows success before the clipboard write
completes, so copy feedback can be false. Update useCopy().copy to handle the
async navigator.clipboard.writeText(text) promise, only call setCopied(true)
after a successful write, and avoid setting copied on failure by
catching/rejecting errors and keeping the current state unchanged or handling an
error state if needed.

---

Nitpick comments:
In `@apps/web/src/components/dither-kit-docs/content.ts`:
- Around line 7-8: Remove the decorative file-level JSDoc summary and any JSDoc
blocks attached to non-exported helpers in content.ts, since only exported
functions/types should use JSDoc. Also replace the ASCII section divider
comments with whitespace and clearer naming, keeping the structure readable
without decorative separators; update the affected helpers and section markers
consistently throughout the file.

In `@apps/web/src/components/dither-kit-docs/sections.tsx`:
- Around line 63-77: Extract the inline props object type from HeroSection into
a named PascalCase interface (for example, HeroSectionProps) and use it in the
function signature; apply the same pattern to InstallSection and ChartGallery so
all component props follow the project guideline of defining explicit props
interfaces. Keep the interface definitions close to their respective components
in sections.tsx and ensure the names are suffixed with Props for easy
identification.
- Line 51: The comment in sections.tsx is a decorative JSDoc block that is not
documenting an exported function/type and also contains an em dash. Remove it or
replace it with a simple plain single-line comment, and keep comments sparse and
non-decorative; use JSDoc only for exported symbols such as functions or types.

In `@apps/web/src/components/dither-kit-docs/ui.tsx`:
- Around line 16-22: Replace the inline style object in the dither-kit docs UI
component with Tailwind arbitrary-value classes, since the gradient and mask
values are static. Update the relevant JSX in the UI component to express the
background image, background size, and mask image using Tailwind-only className
utilities instead of the style prop, keeping the same visual effect.
- Around line 6-7: The comments in ui.tsx should be brought in line with repo
style by removing the decorative file-level doc comment, all ASCII divider
comments, and the JSDoc on the non-exported tokenize helper. Also rewrite the
remaining comments to be sparse and plain, eliminating em dashes and overly
verbose phrasing while keeping only any truly necessary inline notes near the
relevant symbols such as tokenize and the other component helpers.
- Line 11: Replace the anonymous inline prop type annotations in DitherStrip,
CopyLine, Pill, ReplayButton, and Showcase with named props interfaces defined
in the same module. Update each component signature to destructure from its
interface type instead of a raw inline object, and make Showcase’s multi-field
props especially use a dedicated interface to match the component props
guideline.
- Around line 211-215: The conditional class logic in Pill and Showcase is using
raw template-literal ternaries instead of the shared cn() helper. Update the
relevant className construction in Pill and the tall-driven height handling in
Showcase to use cn() from `@tripwire/ui/utils` for conditional merging, keeping
the same active/tall behavior while moving the branching into cn()-based
arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 61731bd1-e9b6-4327-bdf7-5730763c14df

📥 Commits

Reviewing files that changed from the base of the PR and between 84b88fa and f91cd7b.

📒 Files selected for processing (5)
  • apps/web/biome.json
  • apps/web/src/components/dither-kit-docs/content.ts
  • apps/web/src/components/dither-kit-docs/sections.tsx
  • apps/web/src/components/dither-kit-docs/ui.tsx
  • apps/web/src/routes/dither-kit.tsx

Comment on lines +113 to +114
const durationAttr = (t: Tweaks): string =>
t.duration === 900 ? "" : ` animationDuration={${t.duration}}`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## content.ts context\n'
sed -n '90,140p' apps/web/src/components/dither-kit-docs/content.ts | cat -n

printf '\n## dither-kit.tsx context\n'
sed -n '1,220p' apps/web/src/routes/dither-kit.tsx | cat -n

printf '\n## search for entranceMs and 900 defaults\n'
rg -n "entranceMs|900" apps/web/src/components/dither-kit-docs/content.ts apps/web/src/routes/dither-kit.tsx

Repository: bountydotnew/tripwire

Length of output: 7403


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## content.ts outline\n'
ast-grep outline apps/web/src/components/dither-kit-docs/content.ts --view expanded || true

printf '\n## dither-kit.tsx outline\n'
ast-grep outline apps/web/src/routes/dither-kit.tsx --view expanded || true

Repository: bountydotnew/tripwire

Length of output: 751


Keep the entrance duration default in one place. apps/web/src/components/dither-kit-docs/content.ts and apps/web/src/routes/dither-kit.tsx both hardcode 900, so the generated snippet can drift from the live default. Extract a shared DEFAULT_ENTRANCE_MS and use it in both spots.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/content.ts` around lines 113 - 114,
The entrance duration default is hardcoded in both the docs snippet generator
and the live route, which can cause them to drift. Extract a shared
DEFAULT_ENTRANCE_MS constant and use it in content.ts’s durationAttr helper and
in the dither-kit route’s default value so both references stay synchronized.

Comment on lines +89 to +100
<button
type="button"
aria-label={light ? "Switch to dark theme" : "Switch to light theme"}
onClick={onToggleTheme}
className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
>
{light ? (
<MoonIcon className="size-4" />
) : (
<SunIcon className="size-4" />
)}
</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm an icon/button primitive is exported from the ui barrel
fd -t f 'index.*' apps/web/src/components/ui | head
rg -nP '\bexport\s+.*\bButton\b' apps/web/src/components/ui

Repository: bountydotnew/tripwire

Length of output: 340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Top-level files/dirs:\n'
fd -t d -d 2 . | sed 's#^\./##' | sort | head -200

printf '\nLocate reviewed file:\n'
fd -t f 'sections.tsx' . | sed 's#^\./##'

printf '\nLocate Button exports / ui barrel files:\n'
fd -t f 'index.*' . | rg '/(components/ui|components/ui/|ui)/' || true
rg -n --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' '\bexport\b.*\bButton\b|\bexport\b.*\bbutton\b' .

printf '\nLocate raw button usage near dither-kit-docs:\n'
rg -n --glob 'apps/web/src/components/dither-kit-docs/**' '<button|Button' apps/web/src/components/dither-kit-docs || true

Repository: bountydotnew/tripwire

Length of output: 2299


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'packages/ui/src/button.tsx (outline + first 220 lines):\n'
ast-grep outline packages/ui/src/button.tsx --view expanded || true
sed -n '1,220p' packages/ui/src/button.tsx | cat -n

printf '\napps/web/src/components/dither-kit-docs/sections.tsx (top 140 lines):\n'
sed -n '1,140p' apps/web/src/components/dither-kit-docs/sections.tsx | cat -n

printf '\napps/web/src/components/dither-kit-docs/ui.tsx (button-related slices):\n'
sed -n '1,320p' apps/web/src/components/dither-kit-docs/ui.tsx | cat -n | sed -n '1,320p'

Repository: bountydotnew/tripwire

Length of output: 23085


Use the shared Button primitive for the theme toggle. The UI package already exports Button with icon sizes/ghost styling, so this raw <button> can be replaced without changing behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/sections.tsx` around lines 89 - 100,
The theme toggle in sections.tsx is using a raw <button> instead of the shared
Button primitive. Replace the existing button inside the theme toggle component
with the exported Button component from the UI package, preserving the same
aria-label, onClick handler, and MoonIcon/SunIcon rendering while applying the
appropriate icon/ghost styling through Button props or variant classes.

Source: Coding guidelines

Comment on lines +71 to +79
function useCopy() {
const [copied, setCopied] = useState(false)
const copy = (text: string) => {
navigator.clipboard?.writeText(text)
setCopied(true)
setTimeout(() => setCopied(false), 1400)
}
return { copied, copy }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Copy feedback fires even when the clipboard write fails.

navigator.clipboard?.writeText(text) isn't awaited/handled, so setCopied(true) runs unconditionally — including when clipboard access is unavailable or the write promise rejects, giving users false "copied" feedback.

🐛 Suggested fix
 function useCopy() {
   const [copied, setCopied] = useState(false)
   const copy = (text: string) => {
-    navigator.clipboard?.writeText(text)
-    setCopied(true)
-    setTimeout(() => setCopied(false), 1400)
+    navigator.clipboard
+      ?.writeText(text)
+      .then(() => {
+        setCopied(true)
+        setTimeout(() => setCopied(false), 1400)
+      })
+      .catch(() => {})
   }
   return { copied, copy }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function useCopy() {
const [copied, setCopied] = useState(false)
const copy = (text: string) => {
navigator.clipboard?.writeText(text)
setCopied(true)
setTimeout(() => setCopied(false), 1400)
}
return { copied, copy }
}
function useCopy() {
const [copied, setCopied] = useState(false)
const copy = (text: string) => {
navigator.clipboard
?.writeText(text)
.then(() => {
setCopied(true)
setTimeout(() => setCopied(false), 1400)
})
.catch(() => {})
}
return { copied, copy }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 71 - 79, The
useCopy hook shows success before the clipboard write completes, so copy
feedback can be false. Update useCopy().copy to handle the async
navigator.clipboard.writeText(text) promise, only call setCopied(true) after a
successful write, and avoid setting copied on failure by catching/rejecting
errors and keeping the current state unchanged or handling an error state if
needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant