Skip to content

feat: upgrade Next.js deps and refresh settings modal - #48

Merged
AruNi-01 merged 1 commit into
mainfrom
deps/upgrade-nextjs-actions-ver
Mar 19, 2026
Merged

AruNi-01 merged 1 commit into
mainfrom
deps/upgrade-nextjs-actions-ver

Conversation

@AruNi-01

@AruNi-01 AruNi-01 commented Mar 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Upgrade all Next.js apps in the monorepo to 16.2.0 and align eslint-config-next versions
  • Update GitHub Actions workflows from actions/checkout@v4 to @v5 for Node 24 compatibility
  • Refresh lockfiles after dependency updates
  • Fix streamdown type incompatibilities in @workspace/ui after dependency resolution changes
  • Add stable id and order props to the main resizable panel layout to address dynamic panel warnings
  • Redesign the topbar Settings modal:
    • larger modal size
    • left sidebar navigation scaffold for future settings sections
    • tighter About header spacing
    • shared ATMOS wordmark component reused from Welcome
    • flatter settings-style content layout

Related Issue

Closes #44

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Chore / tooling

Validation

  • just lint
  • just test
  • just fmt
  • Additional checks (describe below)

Additional checks:

  • bun install
  • bun run typecheck in apps/web
  • bun run build in apps/web
  • bun run typecheck in packages/ui
  • bun run typecheck in apps/landing
  • bun run build in apps/landing
  • bun run types:check in apps/docs
  • bun run build in apps/docs

Checklist

  • I updated documentation if behavior changed
  • I added/updated tests where appropriate
  • I followed repository conventions and AGENTS.md guidance

Open with Devin

Summary by cubic

Upgrade the monorepo to Next.js 16.2.0 and Node 24–ready Actions, and refresh the Settings modal with a new sidebar layout. Fixes panel warnings and streamdown types; aligns eslint-config-next. Closes #44.

  • New Features

    • Redesigned Settings modal: larger canvas, left sidebar scaffold, tighter About header, shared AtmosWordmark, flatter settings-style layout, and structured desktop update checks (runtime, version, status).
    • Resizable layout: add stable id and order on panels to stop dynamic panel warnings.
    • Reuse AtmosWordmark on Welcome.
  • Dependencies

    • Bump next to 16.2.0 across apps/web, apps/landing, apps/docs; align eslint-config-next to 16.2.0.
    • Update GitHub Actions to actions/checkout@v5 for Node 24 compatibility.
    • Refresh lockfiles; bump desktop to 0.2.1.
    • Fix streamdown plugin type compatibility in @workspace/ui.

Written for commit ddb8905. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Redesigned Settings Modal with a new two-column layout featuring section navigation and improved organization.
    • Added Runtime and Version information display in the settings interface.
    • Introduced new Atmosphere wordmark component for consistent branding.
  • Chores

    • Updated Next.js to version 16.2.0.
    • Updated GitHub Actions checkout action to v5 across CI workflows.

@vercel

vercel Bot commented Mar 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
atmos-landing Ready Ready Preview, Comment Mar 19, 2026 3:09am

@coderabbitai

coderabbitai Bot commented Mar 19, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR updates GitHub Actions workflows from actions/checkout@v4 to v5 across all CI pipelines, upgrades Next.js dependencies from 16.1.x to 16.2.0, refactors the SettingsModal to a two-column sectioned layout, extracts the wordmark into a reusable component, and adds explicit type annotations for Streamdown plugin configurations.

Changes

Cohort / File(s) Summary
GitHub Actions Workflow Updates
.github/workflows/ci-*.yml, .github/workflows/release-desktop.yml, .github/workflows/sync-homebrew-tap.yml
Updated actions/checkout from v4 to v5 across all CI and release workflows. No changes to job logic, commands, or dependencies.
Next.js Dependency Upgrades
package.json, apps/docs/package.json, apps/landing/package.json, apps/web/package.json
Bumped next and eslint-config-next from 16.1.x to 16.2.0 across root and app packages.
SettingsModal Component Refactor
apps/web/src/components/dialogs/SettingsModal.tsx
Restructured dialog from single-column to two-column grid layout with left sidebar navigation. Added SETTINGS_SECTIONS constant and activeSection state. Replaced header/logo with new Runtime/Version display and reorganized update controls into dedicated sections. Updated icon imports and added DialogDescription.
Wordmark Component Extraction
apps/web/src/components/ui/AtmosWordmark.tsx, apps/web/src/components/welcome/WelcomePage.tsx
Created new reusable AtmosWordmark component for rendering styled wordmark with slogan. Refactored WelcomePage to use new component, removing inline markup and imports for GeistPixelCircle and LogoSvg.
Layout Panel Enhancements
apps/web/src/components/layout/PanelLayout.tsx
Added explicit id and order attributes to left, center, and right Panel components for improved panel management and layout control.
Type Annotations for Streamdown Plugins
packages/ui/src/components/ai-elements/message.tsx, packages/ui/src/components/ai-elements/reasoning.tsx
Added explicit PluginConfig type annotation to streamdownPlugins constant and applied type assertion to code plugin to resolve type compatibility with Streamdown's plugin contract.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Poem

🐰 We've tidied up the Actions, bumped our Next.js way up high,
Extracted wordmarks into components beneath the digital sky,
The SettingsModal got a makeover—left sidebar, sections galore,
While types now dance with Streamdown plugins, clean and structured to the core! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Multiple significant changes appear unrelated to issue #44's quick update feature: Next.js version upgrades, actions/checkout updates, new AtmosWordmark component, PanelLayout refactoring, and streamdown type fixes. Consider separating the PR into focused changes: one for quick update feature, one for dependency upgrades, and one for UI refactoring to improve review clarity.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR addresses issue #44 requirements by implementing update-related changes in SettingsModal and workflows for Node 24 compatibility, but adds extensive unrelated refactoring beyond the quick update feature. Clarify whether the extensive settings modal redesign and component extraction are necessary for the quick update feature, or if they should be addressed in a separate PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately summarizes the main changes: upgrading Next.js dependencies and redesigning the settings modal, which are the most significant modifications in the changeset.
Description check ✅ Passed The PR summary comprehensively documents all changes including dependency upgrades, workflow updates, modal redesign, and type fixes, providing sufficient context for review.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch deps/upgrade-nextjs-actions-ver
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

You can get early access to new features in CodeRabbit.

Enable the early_access setting to enable early access features such as new models, tools, and more.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 19 files

@devin-ai-integration devin-ai-integration 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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@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: 2

🧹 Nitpick comments (5)
packages/ui/src/components/ai-elements/message.tsx (1)

32-32: Extract streamdownPlugins into a shared ai-elements module to prevent drift.

MessageResponse and ReasoningContent now carry the same plugin map + cast. Centralize this into one exported constant so type-workaround changes happen once.

♻️ Proposed refactor
+// packages/ui/src/components/ai-elements/streamdown-plugins.ts
+import { cjk } from "@streamdown/cjk";
+import { code } from "@streamdown/code";
+import { math } from "@streamdown/math";
+import { mermaid } from "@streamdown/mermaid";
+import type { PluginConfig } from "streamdown";
+
+export const streamdownPlugins: PluginConfig = {
+  cjk,
+  code: code as PluginConfig["code"],
+  math,
+  mermaid,
+};
- import { Streamdown, type PluginConfig } from "streamdown";
+ import { Streamdown } from "streamdown";
+ import { streamdownPlugins } from "./streamdown-plugins";
...
- const streamdownPlugins: PluginConfig = { cjk, code: code as PluginConfig["code"], math, mermaid };

As per coding guidelines: "Keep components atomic and reusable".

Also applies to: 325-327

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/ai-elements/message.tsx` at line 32, The plugin
map and type-cast for Streamdown are duplicated in MessageResponse and
ReasoningContent; extract the shared configuration into a single exported
constant (e.g., streamdownPlugins) in the ai-elements module and import it into
the component file so both MessageResponse and ReasoningContent use that
constant instead of local, duplicated plugin definitions; update any type
assertions to reference the centralized export to avoid future drift and ensure
only one place needs changes.
package.json (1)

24-36: Pin the Node floor with this Next 16 bump.

Next.js 16 now requires Node.js 20.9.0+, so upgrading the shared catalog without an engines.node guard leaves unsupported local environments to fail later during install/build instead of immediately. Adding the floor at the root manifest will make this upgrade much less surprising for contributors. (nextjs.org)

♻️ Proposed fix
 {
   "name": "atmos",
   "private": false,
+  "engines": {
+    "node": ">=20.9.0"
+  },
   "workspaces": [
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 24 - 36, The package.json update bumped Next to
"16.2.0" but did not add an engines.node floor; add an engines field at the root
of package.json with "node": ">=20.9.0" (or ">=20.9.0 <21" if you prefer a
strict upper bound) so installs/builds fail fast on unsupported Node
versions—locate the root manifest near the "next": "16.2.0" dependency and add
the engines.node entry.
apps/web/src/components/ui/AtmosWordmark.tsx (1)

1-78: Move this out of the app-local ui/ layer.

This is product branding, not a reusable primitive. Keeping it under src/components/ui makes the app-local UI folder compete with @workspace/ui; a components/brand/ or similar location would fit the repo boundary better.

As per coding guidelines, "apps/web/src/components/**/*.{ts,tsx}: Generic UI components should be consumed from @workspace/ui; business-specific components should live in src/components/."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/AtmosWordmark.tsx` around lines 1 - 78, The
AtmosWordmark component is product branding and should be moved out of the
app-local ui layer; relocate the AtmosWordmark React component (the exported
AtmosWordmark symbol) from the current ui folder into a business-specific
location such as components/brand/, update any imports across the app that
reference AtmosWordmark to the new path, and ensure its module export remains
the same so consumers still import { AtmosWordmark } without changing its API;
also remove or adjust any barrel exports in the old ui index so the app-local ui
folder no longer exposes this branding component.
apps/web/src/components/dialogs/SettingsModal.tsx (2)

84-105: Expose the active section semantically.

These buttons swap content in place, but the selected state is only visual right now. Please give the sidebar tab semantics (role="tablist" / role="tab" plus aria-selected / aria-controls) or an equivalent pattern so keyboard and screen-reader users can track which section is active.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/dialogs/SettingsModal.tsx` around lines 84 - 105,
Wrap the sidebar navigation (the nav rendering SETTINGS_SECTIONS) with
role="tablist" and make each section button a proper tab by adding role="tab", a
stable id like `${section.id}-tab`, aria-selected={isActive}, and
aria-controls={`${section.id}-panel`} (keep the existing onClick that calls
setActiveSection). Then ensure the corresponding content container that renders
the activeSection has role="tabpanel", an id matching `${section.id}-panel`, and
aria-labelledby pointing to `${section.id}-tab` so screen readers and keyboard
users can track and focus the activeSection (use activeSection to set which
panel is visible/focused).

181-185: Avoid the hardcoded success green here.

text-green-500 is the only raw color in this modal, so it will drift from the design tokens in light/dark themes. Please swap it to the semantic success/positive token the design system already uses.

As per coding guidelines, "apps/web/src/components/**/*.{ts,tsx}: ALWAYS use semantic CSS variables (bg-background, text-muted-foreground, border-border, etc.) instead of hardcoded Tailwind colors like bg-zinc-900 or text-gray-500 for layout components`."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/dialogs/SettingsModal.tsx` around lines 181 - 185, In
SettingsModal, replace the hardcoded Tailwind color on the success state (the
Check JSX element using className "size-4 text-green-500") with the
design-system semantic success token (e.g., "text-success-foreground" or your
project's equivalent) so it uses the theme-aware CSS variable instead of a raw
color; update the Check component's className and any adjacent "Up to date"
styling to use the semantic token (refer to existing tokens like
"text-muted-foreground" for naming consistency).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/dialogs/SettingsModal.tsx`:
- Line 67: The dialog's grid uses fixed 240px and repeated 320px column classes
which cause clipping on narrow viewports; update the grid classnames in
SettingsModal (the DialogContent wrapper and the internal grid containers that
define the 240px sidebar + 320px columns) to switch to a single-column layout on
small screens (e.g., grid-cols-1) and only apply the multi-column (240px + 320px
repeats) layout at lg/breakpoint sizes (e.g., lg:grid-cols-...), so the sidebar
and content stack and reflow instead of being cropped; apply this change to the
same grid class usages referenced (the DialogContent and the internal grids
around the 240px sidebar and 320px columns).

In `@apps/web/src/components/ui/AtmosWordmark.tsx`:
- Around line 22-67: The wordmark currently renders as separate letters and an
SVG which screen readers won’t read as “Atmos”; update the AtmosWordmark
component so the decorative row (the container div that holds the letter spans
and LogoSvg) is marked decorative by adding aria-hidden="true" (or
role="presentation") to that element, and add a visually-hidden text node for
the product name (e.g., a span with a visually-hidden/sr-only class containing
"Atmos") at the start or end of the component so assistive tech will announce
the brand; target the container that includes the letter spans and the LogoSvg
and add the hidden span alongside it.

---

Nitpick comments:
In `@apps/web/src/components/dialogs/SettingsModal.tsx`:
- Around line 84-105: Wrap the sidebar navigation (the nav rendering
SETTINGS_SECTIONS) with role="tablist" and make each section button a proper tab
by adding role="tab", a stable id like `${section.id}-tab`,
aria-selected={isActive}, and aria-controls={`${section.id}-panel`} (keep the
existing onClick that calls setActiveSection). Then ensure the corresponding
content container that renders the activeSection has role="tabpanel", an id
matching `${section.id}-panel`, and aria-labelledby pointing to
`${section.id}-tab` so screen readers and keyboard users can track and focus the
activeSection (use activeSection to set which panel is visible/focused).
- Around line 181-185: In SettingsModal, replace the hardcoded Tailwind color on
the success state (the Check JSX element using className "size-4
text-green-500") with the design-system semantic success token (e.g.,
"text-success-foreground" or your project's equivalent) so it uses the
theme-aware CSS variable instead of a raw color; update the Check component's
className and any adjacent "Up to date" styling to use the semantic token (refer
to existing tokens like "text-muted-foreground" for naming consistency).

In `@apps/web/src/components/ui/AtmosWordmark.tsx`:
- Around line 1-78: The AtmosWordmark component is product branding and should
be moved out of the app-local ui layer; relocate the AtmosWordmark React
component (the exported AtmosWordmark symbol) from the current ui folder into a
business-specific location such as components/brand/, update any imports across
the app that reference AtmosWordmark to the new path, and ensure its module
export remains the same so consumers still import { AtmosWordmark } without
changing its API; also remove or adjust any barrel exports in the old ui index
so the app-local ui folder no longer exposes this branding component.

In `@package.json`:
- Around line 24-36: The package.json update bumped Next to "16.2.0" but did not
add an engines.node floor; add an engines field at the root of package.json with
"node": ">=20.9.0" (or ">=20.9.0 <21" if you prefer a strict upper bound) so
installs/builds fail fast on unsupported Node versions—locate the root manifest
near the "next": "16.2.0" dependency and add the engines.node entry.

In `@packages/ui/src/components/ai-elements/message.tsx`:
- Line 32: The plugin map and type-cast for Streamdown are duplicated in
MessageResponse and ReasoningContent; extract the shared configuration into a
single exported constant (e.g., streamdownPlugins) in the ai-elements module and
import it into the component file so both MessageResponse and ReasoningContent
use that constant instead of local, duplicated plugin definitions; update any
type assertions to reference the centralized export to avoid future drift and
ensure only one place needs changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4081d3a1-af62-4e69-85d0-8b6ebb92b3ae

📥 Commits

Reviewing files that changed from the base of the PR and between 5a905ca and ddb8905.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .github/workflows/ci-backend.yml
  • .github/workflows/ci-docs.yml
  • .github/workflows/ci-landing.yml
  • .github/workflows/ci-packages.yml
  • .github/workflows/ci-web.yml
  • .github/workflows/release-desktop.yml
  • .github/workflows/sync-homebrew-tap.yml
  • apps/docs/package.json
  • apps/landing/package.json
  • apps/web/package.json
  • apps/web/src/components/dialogs/SettingsModal.tsx
  • apps/web/src/components/layout/PanelLayout.tsx
  • apps/web/src/components/ui/AtmosWordmark.tsx
  • apps/web/src/components/welcome/WelcomePage.tsx
  • package.json
  • packages/ui/src/components/ai-elements/message.tsx
  • packages/ui/src/components/ai-elements/reasoning.tsx

return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-lg">
<DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]">

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.

⚠️ Potential issue | 🟠 Major

The fixed grids will clip on narrow viewports.

The dialog can shrink to 96vw, but this layout hard-codes a 240px sidebar and repeated 320px value columns. On smaller windows that leaves no usable width for the content column, and overflow-hidden on DialogContent will just crop the UI instead of reflowing it.

♻️ Proposed fix
-      <DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]">
+      <DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]">
@@
-        <div className="grid h-full grid-cols-[240px_minmax(0,1fr)]">
-          <aside className="flex h-full flex-col border-r border-border bg-muted/20">
+        <div className="grid h-full grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)]">
+          <aside className="flex flex-col border-b border-border bg-muted/20 lg:h-full lg:border-b-0 lg:border-r">
@@
-          <section className="flex min-h-0 flex-col">
+          <section className="flex min-h-0 min-w-0 flex-col">
@@
-                <div className="grid grid-cols-[minmax(0,1fr)_320px] gap-8 border-b border-border px-6 py-5">
+                <div className="grid grid-cols-1 gap-4 border-b border-border px-6 py-5 lg:grid-cols-[minmax(0,1fr)_320px] lg:gap-8">

Apply the same grid-cols-1 -> lg:grid-cols[...] pattern to the repeated 320px rows below.

Also applies to: 73-74, 125-126, 138-146, 152-160, 173-180

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/dialogs/SettingsModal.tsx` at line 67, The dialog's
grid uses fixed 240px and repeated 320px column classes which cause clipping on
narrow viewports; update the grid classnames in SettingsModal (the DialogContent
wrapper and the internal grid containers that define the 240px sidebar + 320px
columns) to switch to a single-column layout on small screens (e.g.,
grid-cols-1) and only apply the multi-column (240px + 320px repeats) layout at
lg/breakpoint sizes (e.g., lg:grid-cols-...), so the sidebar and content stack
and reflow instead of being cropped; apply this change to the same grid class
usages referenced (the DialogContent and the internal grids around the 240px
sidebar and 320px columns).

Comment on lines +22 to +67
<div className={cn('flex flex-col items-center', className)}>
<div
className={cn(
'group flex w-full max-w-3xl items-center justify-between cursor-default select-none',
GeistPixelCircle.className
)}
>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
A
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
t
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
m
</span>
<LogoSvg
className={cn(
'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90',
logoClassName
)}
/>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
s
</span>
</div>

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.

⚠️ Potential issue | 🟡 Minor

Expose the brand name to assistive tech.

Because the wordmark is rendered as individual letters plus a decorative SVG, assistive tech will not reliably announce “Atmos” from this markup. Add hidden text for the product name and mark the stylized letter row as decorative.

♿ Proposed fix
   return (
     <div className={cn('flex flex-col items-center', className)}>
+      <span className="sr-only">Atmos</span>
       <div
+        aria-hidden="true"
         className={cn(
           'group flex w-full max-w-3xl items-center justify-between cursor-default select-none',
           GeistPixelCircle.className
📝 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
<div className={cn('flex flex-col items-center', className)}>
<div
className={cn(
'group flex w-full max-w-3xl items-center justify-between cursor-default select-none',
GeistPixelCircle.className
)}
>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
A
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
t
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
m
</span>
<LogoSvg
className={cn(
'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90',
logoClassName
)}
/>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
s
</span>
</div>
<div className={cn('flex flex-col items-center', className)}>
<span className="sr-only">Atmos</span>
<div
aria-hidden="true"
className={cn(
'group flex w-full max-w-3xl items-center justify-between cursor-default select-none',
GeistPixelCircle.className
)}
>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
A
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
t
</span>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
m
</span>
<LogoSvg
className={cn(
'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90',
logoClassName
)}
/>
<span
className={cn(
'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm',
letterClassName
)}
>
s
</span>
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/AtmosWordmark.tsx` around lines 22 - 67, The
wordmark currently renders as separate letters and an SVG which screen readers
won’t read as “Atmos”; update the AtmosWordmark component so the decorative row
(the container div that holds the letter spans and LogoSvg) is marked decorative
by adding aria-hidden="true" (or role="presentation") to that element, and add a
visually-hidden text node for the product name (e.g., a span with a
visually-hidden/sr-only class containing "Atmos") at the start or end of the
component so assistive tech will announce the brand; target the container that
includes the letter spans and the LogoSvg and add the hidden span alongside it.

This branch was successfully deployed

1 active deployment
Preview — ddb89057 Deployed Mar 19, 2026 by vercel[bot]
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.

Feat: Desktop App Quick update

1 participant