authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-22 12:22:10-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-22 12:22:10-07:00
log1a81ea8c153214ddc74b7046062a8957c2612e8d
tree178b0d82c8e5c09c92912643ed4f364f8d3a3769
parentc0490e9d0eb057218bfa5df3f25a7c26c37145a5
signature Signed by PGP key B5690EEEBB952194

refactor(web): fold repeated overrides into ui defaults (#13021)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

14 files changed, 23 insertions(+), 31 deletions(-)

apps/web/src/components/NoActiveThreadState.tsx+2-2
...@@ -22,8 +22,8 @@ export function NoActiveThreadState() {...@@ -22,8 +22,8 @@ export function NoActiveThreadState() {
22 <Empty className="flex-1">22 <Empty className="flex-1">
23 <div className="w-full max-w-lg px-8 py-12">23 <div className="w-full max-w-lg px-8 py-12">
24 <EmptyHeader className="max-w-none">24 <EmptyHeader className="max-w-none">
25 <EmptyTitle className="text-foreground">Pick a thread to continue</EmptyTitle>25 <EmptyTitle>Pick a thread to continue</EmptyTitle>
26 <EmptyDescription className="mt-2 text-muted-foreground/78">26 <EmptyDescription>
27 Select an existing thread or create a new one to get started.27 Select an existing thread or create a new one to get started.
28 </EmptyDescription>28 </EmptyDescription>
29 </EmptyHeader>29 </EmptyHeader>
apps/web/src/components/NoProjectsHero.tsx+2-6
...@@ -15,12 +15,8 @@ export function NoProjectsHero() {...@@ -15,12 +15,8 @@ export function NoProjectsHero() {
15 <Empty className="flex-1">15 <Empty className="flex-1">
16 <div className="w-full max-w-lg px-8 py-12">16 <div className="w-full max-w-lg px-8 py-12">
17 <EmptyHeader className="max-w-none">17 <EmptyHeader className="max-w-none">
18 <EmptyTitle className="text-foreground text-2xl sm:text-3xl">18 <EmptyTitle className="text-2xl sm:text-3xl">What should we work on?</EmptyTitle>
19 What should we work on?19 <EmptyDescription>Add a project to start your first thread.</EmptyDescription>
20 </EmptyTitle>
21 <EmptyDescription className="mt-2 text-muted-foreground/78">
22 Add a project to start your first thread.
23 </EmptyDescription>
24 <div className="mt-6 flex justify-center">20 <div className="mt-6 flex justify-center">
25 <Button size="sm" onClick={openAddProject}>21 <Button size="sm" onClick={openAddProject}>
26 <PlusIcon className="size-4" />22 <PlusIcon className="size-4" />
apps/web/src/components/ProjectScriptsControl.tsx+3-3
...@@ -157,7 +157,7 @@ export default function ProjectScriptsControl({...@@ -157,7 +157,7 @@ export default function ProjectScriptsControl({
157 onClick={() => void importFileScript(fileScript)}157 onClick={() => void importFileScript(fileScript)}
158 >158 >
159 <ScriptIcon icon={fileScript.icon ?? "play"} className="size-4" />159 <ScriptIcon icon={fileScript.icon ?? "play"} className="size-4" />
160 <MenuItemLabel className="truncate">{fileScript.name}</MenuItemLabel>160 <MenuItemLabel>{fileScript.name}</MenuItemLabel>
161 <MenuShortcut>161 <MenuShortcut>
162 <DownloadIcon className="size-3.5" aria-label="Import" />162 <DownloadIcon className="size-3.5" aria-label="Import" />
163 </MenuShortcut>163 </MenuShortcut>
...@@ -182,7 +182,7 @@ export default function ProjectScriptsControl({...@@ -182,7 +182,7 @@ export default function ProjectScriptsControl({
182 onClick={() => onRunScript(script)}182 onClick={() => onRunScript(script)}
183 >183 >
184 <ScriptIcon icon={script.icon} className="size-4" />184 <ScriptIcon icon={script.icon} className="size-4" />
185 <MenuItemLabel className="truncate">185 <MenuItemLabel>
186 {script.runOnWorktreeCreate ? `${script.name} (setup)` : script.name}186 {script.runOnWorktreeCreate ? `${script.name} (setup)` : script.name}
187 </MenuItemLabel>187 </MenuItemLabel>
188 <span className="relative ms-auto flex h-6 min-w-6 items-center justify-end">188 <span className="relative ms-auto flex h-6 min-w-6 items-center justify-end">
...@@ -241,7 +241,7 @@ export default function ProjectScriptsControl({...@@ -241,7 +241,7 @@ export default function ProjectScriptsControl({
241 onClick={() => onRunScript(primaryScript)}241 onClick={() => onRunScript(primaryScript)}
242 >242 >
243 <ScriptIcon icon={primaryScript.icon} className="size-4" />243 <ScriptIcon icon={primaryScript.icon} className="size-4" />
244 <MenuItemLabel className="truncate">Run {primaryScript.name}</MenuItemLabel>244 <MenuItemLabel>Run {primaryScript.name}</MenuItemLabel>
245 <MenuShortcut>245 <MenuShortcut>
246 {shortcutLabelForCommand(keybindings, commandForProjectScript(primaryScript.id))}246 {shortcutLabelForCommand(keybindings, commandForProjectScript(primaryScript.id))}
247 </MenuShortcut>247 </MenuShortcut>
apps/web/src/components/chat/OpenInPicker.tsx+1-1
...@@ -332,7 +332,7 @@ export const OpenInPicker = memo(function OpenInPicker({...@@ -332,7 +332,7 @@ export const OpenInPicker = memo(function OpenInPicker({
332 onClick={() => openInEditor(preferredEditor)}332 onClick={() => openInEditor(preferredEditor)}
333 >333 >
334 <primaryOption.Icon className={cn("size-4", getOpenInIconClass(primaryOption.kind))} />334 <primaryOption.Icon className={cn("size-4", getOpenInIconClass(primaryOption.kind))} />
335 <MenuItemLabel className="truncate">Open in {primaryOption.label}</MenuItemLabel>335 <MenuItemLabel>Open in {primaryOption.label}</MenuItemLabel>
336 {openFavoriteEditorShortcutLabel && (336 {openFavoriteEditorShortcutLabel && (
337 <MenuShortcut>{openFavoriteEditorShortcutLabel}</MenuShortcut>337 <MenuShortcut>{openFavoriteEditorShortcutLabel}</MenuShortcut>
338 )}338 )}
apps/web/src/components/chat/SnapShotAttachmentDetails.tsx+1-1
...@@ -170,7 +170,7 @@ export function SnapShotContentsButton({...@@ -170,7 +170,7 @@ export function SnapShotContentsButton({
170 viewportClassName="max-h-[min(28rem,70vh)]"170 viewportClassName="max-h-[min(28rem,70vh)]"
171 >171 >
172 <div className="space-y-2">172 <div className="space-y-2">
173 <PopoverTitle className="text-sm leading-5">Accessibility data</PopoverTitle>173 <PopoverTitle>Accessibility data</PopoverTitle>
174 {accessibilityDetails ? (174 {accessibilityDetails ? (
175 <SnapShotAccessibilityData175 <SnapShotAccessibilityData
176 details={accessibilityDetails}176 details={accessibilityDetails}
apps/web/src/components/device/DeviceToolVersions.tsx+1-1
...@@ -45,7 +45,7 @@ export function DeviceToolVersions({...@@ -45,7 +45,7 @@ export function DeviceToolVersions({
45 : "Versions"}45 : "Versions"}
46 </PopoverTrigger>46 </PopoverTrigger>
47 <PopoverPopup align="end" className="w-80">47 <PopoverPopup align="end" className="w-80">
48 <PopoverTitle className="text-sm">{kind ? label : "Device tools"}</PopoverTitle>48 <PopoverTitle>{kind ? label : "Device tools"}</PopoverTitle>
49 {tools ? (49 {tools ? (
50 <div className="mt-4 divide-y divide-border/50">50 <div className="mt-4 divide-y divide-border/50">
51 {(51 {(
apps/web/src/components/pullRequest/PullRequestComposer.tsx+1-1
...@@ -131,7 +131,7 @@ export function PullRequestComposer({...@@ -131,7 +131,7 @@ export function PullRequestComposer({
131 </Toggle>131 </Toggle>
132 </ToggleGroup>132 </ToggleGroup>
133 ) : (133 ) : (
134 <PopoverTitle className="text-sm">134 <PopoverTitle>
135 {mode === "review" ? "Review pull request" : "Comment on pull request"}135 {mode === "review" ? "Review pull request" : "Comment on pull request"}
136 </PopoverTitle>136 </PopoverTitle>
137 )}137 )}
apps/web/src/components/pullRequest/PullRequestListFilters.tsx-1
...@@ -359,7 +359,6 @@ function PullRequestLabelFilter({...@@ -359,7 +359,6 @@ function PullRequestLabelFilter({
359 return (359 return (
360 <MenuCheckboxItem360 <MenuCheckboxItem
361 key={key}361 key={key}
362 className="grid-cols-[1rem_minmax(0,1fr)]"
363 checked={checked}362 checked={checked}
364 onCheckedChange={(next) =>363 onCheckedChange={(next) =>
365 onChange(364 onChange(
apps/web/src/components/ui/empty.tsx+1-1
...@@ -89,7 +89,7 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {...@@ -89,7 +89,7 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
89 return (89 return (
90 <div90 <div
91 className={cn(91 className={cn(
92 "text-muted-foreground text-sm [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 [[data-slot=empty-title]+&]:mt-1",92 "text-muted-foreground text-sm [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 [[data-slot=empty-title]+&]:mt-1 [[data-slot=empty-description]+&]:mt-1",
93 className,93 className,
94 )}94 )}
95 data-slot="empty-description"95 data-slot="empty-description"
apps/web/src/components/ui/menu.tsx+4-2
...@@ -117,7 +117,7 @@ function MenuItemLabel({ className, ...props }: React.ComponentProps<"span">) {...@@ -117,7 +117,7 @@ function MenuItemLabel({ className, ...props }: React.ComponentProps<"span">) {
117 <span117 <span
118 data-slot="menu-item-label"118 data-slot="menu-item-label"
119 className={cn(119 className={cn(
120 "min-w-0 in-data-[density=touch]:[text-box:trim-both_cap_alphabetic] supports-[text-box:trim-both_cap_alphabetic]:in-data-[density=touch]:py-[0.5em]",120 "min-w-0 truncate in-data-[density=touch]:[text-box:trim-both_cap_alphabetic] supports-[text-box:trim-both_cap_alphabetic]:in-data-[density=touch]:py-[0.5em]",
121 className,121 className,
122 )}122 )}
123 {...props}123 {...props}
...@@ -139,7 +139,9 @@ function MenuCheckboxItem({...@@ -139,7 +139,9 @@ function MenuCheckboxItem({
139 checked={checked}139 checked={checked}
140 className={cn(140 className={cn(
141 "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-pointer items-center gap-2 rounded-sm py-1 ps-2 text-base text-foreground outline-none data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",141 "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-pointer items-center gap-2 rounded-sm py-1 ps-2 text-base text-foreground outline-none data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
142 variant === "switch" ? "grid-cols-[1fr_auto] gap-4 pe-1.5" : "grid-cols-[1rem_1fr] pe-4",142 variant === "switch"
143 ? "grid-cols-[1fr_auto] gap-4 pe-1.5"
144 : "grid-cols-[1rem_minmax(0,1fr)] pe-4",
143 className,145 className,
144 )}146 )}
145 data-slot="menu-checkbox-item"147 data-slot="menu-checkbox-item"
apps/web/src/components/ui/popover.tsx+1-1
...@@ -89,7 +89,7 @@ function PopoverClose({ ...props }: PopoverPrimitive.Close.Props) {...@@ -89,7 +89,7 @@ function PopoverClose({ ...props }: PopoverPrimitive.Close.Props) {
89function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {89function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
90 return (90 return (
91 <PopoverPrimitive.Title91 <PopoverPrimitive.Title
92 className={cn("font-semibold text-lg leading-none", className)}92 className={cn("font-semibold text-sm leading-none", className)}
93 data-slot="popover-title"93 data-slot="popover-title"
94 {...props}94 {...props}
95 />95 />
apps/web/src/components/usage/UsagePage.tsx-1
...@@ -794,7 +794,6 @@ function UsageEnvironmentFilter({...@@ -794,7 +794,6 @@ function UsageEnvironmentFilter({
794 key={environment.environmentId}794 key={environment.environmentId}
795 checked={checked}795 checked={checked}
796 closeOnClick={false}796 closeOnClick={false}
797 className="grid-cols-[1rem_minmax(0,1fr)]"
798 onCheckedChange={(nextChecked) => {797 onCheckedChange={(nextChecked) => {
799 const next = new Set(selectedEnvironments.map((entry) => entry.environmentId));798 const next = new Set(selectedEnvironments.map((entry) => entry.environmentId));
800 if (nextChecked) next.add(environment.environmentId);799 if (nextChecked) next.add(environment.environmentId);
apps/web/src/routes/_chat.index.tsx+5-9
...@@ -93,8 +93,8 @@ function DraftStartError({ onRetry }: { readonly onRetry: () => void }) {...@@ -93,8 +93,8 @@ function DraftStartError({ onRetry }: { readonly onRetry: () => void }) {
93 <SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none">93 <SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none">
94 <Empty className="flex-1">94 <Empty className="flex-1">
95 <EmptyHeader className="max-w-md">95 <EmptyHeader className="max-w-md">
96 <EmptyTitle className="text-foreground">Couldn’t start a new thread</EmptyTitle>96 <EmptyTitle>Couldn’t start a new thread</EmptyTitle>
97 <EmptyDescription className="mt-2 text-muted-foreground/78">97 <EmptyDescription>
98 The project is still available. Try opening the draft again.98 The project is still available. Try opening the draft again.
99 </EmptyDescription>99 </EmptyDescription>
100 <div className="mt-5 flex justify-center">100 <div className="mt-5 flex justify-center">
...@@ -139,16 +139,12 @@ function HostedStaticOnboardingState() {...@@ -139,16 +139,12 @@ function HostedStaticOnboardingState() {
139 <div className="mx-auto mb-5 flex size-11 items-center justify-center rounded-xl border border-border/70 bg-background/70 text-muted-foreground">139 <div className="mx-auto mb-5 flex size-11 items-center justify-center rounded-xl border border-border/70 bg-background/70 text-muted-foreground">
140 <LinkIcon className="size-5" />140 <LinkIcon className="size-5" />
141 </div>141 </div>
142 <EmptyTitle className="text-foreground">142 <EmptyTitle>Connect to a computer running T3 Code</EmptyTitle>
143 Connect to a computer running T3 Code143 <EmptyDescription>
144 </EmptyTitle>
145 <EmptyDescription className="mt-2 leading-relaxed text-muted-foreground/78">
146 This app connects to T3 Code running on your computer or a server. Start the T3 Code144 This app connects to T3 Code running on your computer or a server. Start the T3 Code
147 desktop app or command-line server on that machine and keep it running.145 desktop app or command-line server on that machine and keep it running.
148 </EmptyDescription>146 </EmptyDescription>
149 <EmptyDescription className="mt-2 leading-relaxed text-muted-foreground/78">147 <EmptyDescription>{description}</EmptyDescription>
150 {description}
151 </EmptyDescription>
152 <div className="mt-6 flex justify-center">148 <div className="mt-6 flex justify-center">
153 <Button render={<Link to="/settings/connections" />} size="sm">149 <Button render={<Link to="/settings/connections" />} size="sm">
154 <PlusIcon className="size-4" />150 <PlusIcon className="size-4" />
scripts/lint-restyle-ceiling.ts+1-1
...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";
8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the
9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you
10// migrate a file, and delete this script when the rule becomes an error.10// migrate a file, and delete this script when the rule becomes an error.
11export const RESTYLE_CEILING = 1184;11export const RESTYLE_CEILING = 1165;
1212
13const RULE = "shadcn(no-restyle)";13const RULE = "shadcn(no-restyle)";
1414