authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-09 20:54:14-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-09 20:54:14-03:00
log5dcb59ed6c191589e730fb964aabad005466eb6d
tree77babc90b134bf5c63322d7dbfd17643ff73e4e3
parent08e18b7833c9b5d33bfcd1ce8976dcb67c11dde5
signature Signed by PGP key B5690EEEBB952194

fix(ui): keep focus and selection borders visible across the app (#16675)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

79 files changed, 359 insertions(+), 131 deletions(-)

apps/desktop/src/permissions/MacPermissionHelper.ts+1-1
......@@ -52,7 +52,7 @@ body { margin: 0; background: transparent; color: var(--text); font: 15px/22px -
5252#panel { position: relative; margin: 2px; padding: 20px; height: 136px; border: 1px solid var(--line); border-radius: 24px; background: var(--base); }
5353header { font-weight: 600; white-space: nowrap; }
5454button { font: inherit; color: inherit; }
55button:focus-visible { outline: 2px solid #007aff; outline-offset: 3px; }
55button:focus-visible { outline: 2px solid #007aff; outline-offset: -2px; }
5656#close { position: absolute; right: 8px; top: 6px; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%; background: var(--base); font-size: 18px; cursor: pointer; opacity: 0; }
5757#panel:hover #close, #close:focus-visible { opacity: 1; }
5858#app { display: flex; align-items: center; gap: 12px; width: 100%; height: 52px; margin-top: 20px; padding: 8px 12px; border: 0; border-radius: 10px; background: var(--row); cursor: grab; text-align: left; font-size: 16px; font-weight: 600; }
apps/desktop/src/preview/Annotation.css+1-1
......@@ -64,5 +64,5 @@ input:focus-visible,
6464select:focus-visible,
6565textarea:focus-visible {
6666 outline: 2px solid color-mix(in srgb, var(--t3-ring) 72%, transparent);
67 outline-offset: 1px;
67 outline-offset: -2px;
6868}
apps/desktop/src/preview/AnnotationStyles.generated.ts+1-1
......@@ -1,3 +1,3 @@
11// Generated by scripts/build-preview-annotation-css.mjs. Do not edit.
22export const previewAnnotationStyles =
3 '/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties;\n:root, :host {\n --spacing: 0.25rem;\n --text-xs: 0.75rem;\n --text-xs--line-height: calc(1 / 0.75);\n --text-sm: 0.875rem;\n --text-sm--line-height: calc(1.25 / 0.875);\n --text-lg: 1.125rem;\n --text-lg--line-height: calc(1.75 / 1.125);\n --font-weight-medium: 500;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --blur-xl: 24px;\n --default-font-family: var(--t3-font-sans);\n --default-mono-font-family: var(--t3-font-mono);\n}\n*, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n}\nhtml, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, \'Helvetica Neue\', \'Noto Sans\', Arial, sans-serif, \'Apple Color Emoji\', \'Segoe UI Emoji\', \'Segoe UI Symbol\', \'Noto Color Emoji\');\n font-feature-settings: var(--default-font-feature-settings, normal);\n font-variation-settings: var(--default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n}\nhr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n}\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\nh1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n}\na {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n}\nb, strong {\n font-weight: bolder;\n}\ncode, kbd, samp, pre {\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \'Liberation Mono\', \'Courier New\', monospace);\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\n font-size: 1em;\n}\nsmall {\n font-size: 80%;\n}\nsub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\ntable {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n}\n:-moz-focusring:where(:not(iframe)) {\n outline: auto;\n}\nprogress {\n vertical-align: baseline;\n}\nsummary {\n display: list-item;\n}\nol, ul, menu {\n list-style: none;\n}\nimg, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n}\nimg, video {\n max-width: 100%;\n height: auto;\n}\nbutton, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n}\n:where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n}\n:where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n}\n::file-selector-button {\n margin-inline-end: 4px;\n}\n::placeholder {\n opacity: 1;\n}\n@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n}\ntextarea {\n resize: vertical;\n}\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n}\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n}\n::-webkit-calendar-picker-indicator {\n line-height: 1;\n}\n:-moz-ui-invalid {\n box-shadow: none;\n}\nbutton, input:where([type=\'button\'], [type=\'reset\'], [type=\'submit\']), ::file-selector-button {\n appearance: button;\n}\n::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n}\n[hidden]:where(:not([hidden=\'until-found\'])) {\n display: none !important;\n}\n.pointer-events-auto {\n pointer-events: auto;\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.absolute {\n position: absolute;\n}\n.fixed {\n position: fixed;\n}\n.inset-0 {\n inset: 0px;\n}\n.top-1\\/2 {\n top: calc(1 / 2 * 100%);\n}\n.top-2\\.5 {\n top: calc(var(--spacing) * 2.5);\n}\n.right-2 {\n right: calc(var(--spacing) * 2);\n}\n.left-1\\/2 {\n left: calc(1 / 2 * 100%);\n}\n.z-1 {\n z-index: 1;\n}\n.block {\n display: block;\n}\n.flex {\n display: flex;\n}\n.grid {\n display: grid;\n}\n.hidden {\n display: none;\n}\n.inline-flex {\n display: inline-flex;\n}\n.h-7 {\n height: calc(var(--spacing) * 7);\n}\n.h-8 {\n height: calc(var(--spacing) * 8);\n}\n.max-h-24 {\n max-height: calc(var(--spacing) * 24);\n}\n.max-h-\\[calc\\(100vh-16px\\)\\] {\n max-height: calc(100vh - 16px);\n}\n.max-h-\\[min\\(176px\\,calc\\(100vh-180px\\)\\)\\] {\n max-height: min(176px, calc(100vh - 180px));\n}\n.min-h-7 {\n min-height: calc(var(--spacing) * 7);\n}\n.min-h-8 {\n min-height: calc(var(--spacing) * 8);\n}\n.w-6 {\n width: calc(var(--spacing) * 6);\n}\n.w-8 {\n width: calc(var(--spacing) * 8);\n}\n.w-\\[min\\(360px\\,calc\\(100vw-16px\\)\\)\\] {\n width: min(360px, calc(100vw - 16px));\n}\n.w-full {\n width: 100%;\n}\n.max-w-70 {\n max-width: calc(var(--spacing) * 70);\n}\n.min-w-0 {\n min-width: 0px;\n}\n.flex-1 {\n flex: 1;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n}\n.-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n}\n.cursor-grab {\n cursor: grab;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize {\n resize: both;\n}\n.resize-none {\n resize: none;\n}\n.appearance-none {\n appearance: none;\n}\n.grid-cols-\\[22px_minmax\\(0\\,1fr\\)\\] {\n grid-template-columns: 22px minmax(0,1fr);\n}\n.grid-cols-\\[82px_minmax\\(0\\,1fr\\)\\] {\n grid-template-columns: 82px minmax(0,1fr);\n}\n.flex-col {\n flex-direction: column;\n}\n.items-center {\n align-items: center;\n}\n.items-start {\n align-items: flex-start;\n}\n.justify-center {\n justify-content: center;\n}\n.gap-0\\.5 {\n gap: calc(var(--spacing) * 0.5);\n}\n.gap-1 {\n gap: var(--spacing);\n}\n.gap-2 {\n gap: calc(var(--spacing) * 2);\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-y-hidden {\n overflow-y: hidden;\n}\n.rounded-lg {\n border-radius: var(--t3-radius);\n}\n.rounded-md {\n border-radius: calc(var(--t3-radius) - 2px);\n}\n.rounded-xl {\n border-radius: calc(var(--t3-radius) + 4px);\n}\n.border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n}\n.border-0 {\n border-style: var(--tw-border-style);\n border-width: 0px;\n}\n.border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n}\n.border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 1px;\n}\n.border-border {\n border-color: var(--t3-border);\n}\n.border-input {\n border-color: var(--t3-input);\n}\n.border-primary {\n border-color: var(--t3-primary);\n}\n.border-transparent {\n border-color: transparent;\n}\n.border-b-transparent {\n border-bottom-color: transparent;\n}\n.bg-background {\n background-color: var(--t3-background);\n}\n.bg-muted {\n background-color: var(--t3-muted);\n}\n.bg-muted\\/40 {\n background-color: var(--t3-muted);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-muted) 40%, transparent);\n }\n}\n.bg-popover\\/95 {\n background-color: var(--t3-popover);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-popover) 95%, transparent);\n }\n}\n.bg-popover\\/96 {\n background-color: var(--t3-popover);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-popover) 96%, transparent);\n }\n}\n.bg-primary {\n background-color: var(--t3-primary);\n}\n.bg-primary\\/10 {\n background-color: var(--t3-primary);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-primary) 10%, transparent);\n }\n}\n.bg-transparent {\n background-color: transparent;\n}\n.p-0 {\n padding: 0px;\n}\n.p-1 {\n padding: var(--spacing);\n}\n.p-2 {\n padding: calc(var(--spacing) * 2);\n}\n.px-0 {\n padding-inline: 0px;\n}\n.px-1 {\n padding-inline: var(--spacing);\n}\n.px-2 {\n padding-inline: calc(var(--spacing) * 2);\n}\n.px-2\\.5 {\n padding-inline: calc(var(--spacing) * 2.5);\n}\n.px-3 {\n padding-inline: calc(var(--spacing) * 3);\n}\n.py-1 {\n padding-block: var(--spacing);\n}\n.py-1\\.5 {\n padding-block: calc(var(--spacing) * 1.5);\n}\n.py-2 {\n padding-block: calc(var(--spacing) * 2);\n}\n.font-mono {\n font-family: var(--t3-font-mono);\n}\n.font-sans {\n font-family: var(--t3-font-sans);\n}\n.text-lg {\n font-size: var(--text-lg);\n line-height: var(--tw-leading, var(--text-lg--line-height));\n}\n.text-sm {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n}\n.text-xs {\n font-size: var(--text-xs);\n line-height: var(--tw-leading, var(--text-xs--line-height));\n}\n.leading-5 {\n --tw-leading: calc(var(--spacing) * 5);\n line-height: calc(var(--spacing) * 5);\n}\n.font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n}\n.font-medium {\n --tw-font-weight: var(--font-weight-medium);\n font-weight: var(--font-weight-medium);\n}\n.font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n}\n.text-foreground {\n color: var(--t3-foreground);\n}\n.text-muted-foreground {\n color: var(--t3-muted-foreground);\n}\n.text-popover-foreground {\n color: var(--t3-popover-foreground);\n}\n.text-primary {\n color: var(--t3-primary);\n}\n.text-primary-foreground {\n color: var(--t3-primary-foreground);\n}\n.shadow-2xl {\n --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-lg {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-sm {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-xs {\n --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.ring-0 {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.blur {\n --tw-blur: blur(8px);\n filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);\n}\n.backdrop-blur-xl {\n --tw-backdrop-blur: blur(var(--blur-xl));\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n}\n.outline-none {\n --tw-outline-style: none;\n outline-style: none;\n}\n.select-none {\n -webkit-user-select: none;\n user-select: none;\n}\n.placeholder\\:text-muted-foreground::placeholder {\n color: var(--t3-muted-foreground);\n}\n@media (hover: hover) {\n .hover\\:bg-accent:hover {\n background-color: var(--t3-accent);\n }\n .hover\\:bg-primary\\/90:hover {\n background-color: var(--t3-primary);\n }\n @supports (color: color-mix(in lab, red, red)) {\n .hover\\:bg-primary\\/90:hover {\n background-color: color-mix(in oklab, var(--t3-primary) 90%, transparent);\n }\n }\n .hover\\:text-accent-foreground:hover {\n color: var(--t3-accent-foreground);\n }\n}\n.focus\\:border-b-primary:focus {\n border-bottom-color: var(--t3-primary);\n}\n.focus\\:ring-0:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.focus\\:outline-none:focus {\n --tw-outline-style: none;\n outline-style: none;\n}\n.disabled\\:pointer-events-none:disabled {\n pointer-events: none;\n}\n.disabled\\:opacity-60:disabled {\n opacity: 60%;\n}\n:host {\n --t3-font-sans: "DM Sans Variable", "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,\n sans-serif;\n --t3-font-mono: "SF Mono", "SFMono-Regular", "JetBrains Mono", Consolas, "Liberation Mono", Menlo, monospace;\n --t3-radius: 0.625rem;\n --t3-background: white;\n --t3-foreground: oklch(0.269 0 0);\n --t3-popover: white;\n --t3-popover-foreground: oklch(0.269 0 0);\n --t3-primary: oklch(0.488 0.217 264);\n --t3-primary-foreground: white;\n --t3-muted: rgb(0 0 0 / 4%);\n --t3-muted-foreground: oklch(0.556 0 0);\n --t3-accent: rgb(0 0 0 / 4%);\n --t3-accent-foreground: oklch(0.269 0 0);\n --t3-border: rgb(0 0 0 / 8%);\n --t3-input: rgb(0 0 0 / 10%);\n --t3-ring: oklch(0.488 0.217 264);\n color: var(--t3-foreground);\n font-family: var(--t3-font-sans);\n}\n* {\n box-sizing: border-box;\n border-color: var(--t3-border);\n}\nbutton, input, select, textarea {\n font: inherit;\n}\nbutton:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {\n outline: 2px solid var(--t3-ring);\n @supports (color: color-mix(in lab, red, red)) {\n outline: 2px solid color-mix(in srgb, var(--t3-ring) 72%, transparent);\n }\n outline-offset: 1px;\n}\n@property --tw-translate-x {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-leading {\n syntax: "*";\n inherits: false;\n}\n@property --tw-font-weight {\n syntax: "*";\n inherits: false;\n}\n@property --tw-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: "<length>";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: "*";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-blur {\n syntax: "*";\n inherits: false;\n}\n@property --tw-brightness {\n syntax: "*";\n inherits: false;\n}\n@property --tw-contrast {\n syntax: "*";\n inherits: false;\n}\n@property --tw-grayscale {\n syntax: "*";\n inherits: false;\n}\n@property --tw-hue-rotate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-invert {\n syntax: "*";\n inherits: false;\n}\n@property --tw-opacity {\n syntax: "*";\n inherits: false;\n}\n@property --tw-saturate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-sepia {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-drop-shadow-size {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-blur {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-brightness {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-contrast {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-grayscale {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-hue-rotate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-invert {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-opacity {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-saturate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-sepia {\n syntax: "*";\n inherits: false;\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-border-style: solid;\n --tw-leading: initial;\n --tw-font-weight: initial;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-color: initial;\n --tw-shadow-alpha: 100%;\n --tw-inset-shadow: 0 0 #0000;\n --tw-inset-shadow-color: initial;\n --tw-inset-shadow-alpha: 100%;\n --tw-ring-color: initial;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-color: initial;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-inset: initial;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-blur: initial;\n --tw-brightness: initial;\n --tw-contrast: initial;\n --tw-grayscale: initial;\n --tw-hue-rotate: initial;\n --tw-invert: initial;\n --tw-opacity: initial;\n --tw-saturate: initial;\n --tw-sepia: initial;\n --tw-drop-shadow: initial;\n --tw-drop-shadow-color: initial;\n --tw-drop-shadow-alpha: 100%;\n --tw-drop-shadow-size: initial;\n --tw-backdrop-blur: initial;\n --tw-backdrop-brightness: initial;\n --tw-backdrop-contrast: initial;\n --tw-backdrop-grayscale: initial;\n --tw-backdrop-hue-rotate: initial;\n --tw-backdrop-invert: initial;\n --tw-backdrop-opacity: initial;\n --tw-backdrop-saturate: initial;\n --tw-backdrop-sepia: initial;\n }\n }\n}\n';
3 '/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties;\n:root, :host {\n --spacing: 0.25rem;\n --text-xs: 0.75rem;\n --text-xs--line-height: calc(1 / 0.75);\n --text-sm: 0.875rem;\n --text-sm--line-height: calc(1.25 / 0.875);\n --text-lg: 1.125rem;\n --text-lg--line-height: calc(1.75 / 1.125);\n --font-weight-medium: 500;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --blur-xl: 24px;\n --default-font-family: var(--t3-font-sans);\n --default-mono-font-family: var(--t3-font-mono);\n}\n*, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n}\nhtml, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, \'Helvetica Neue\', \'Noto Sans\', Arial, sans-serif, \'Apple Color Emoji\', \'Segoe UI Emoji\', \'Segoe UI Symbol\', \'Noto Color Emoji\');\n font-feature-settings: var(--default-font-feature-settings, normal);\n font-variation-settings: var(--default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n}\nhr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n}\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\nh1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n}\na {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n}\nb, strong {\n font-weight: bolder;\n}\ncode, kbd, samp, pre {\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \'Liberation Mono\', \'Courier New\', monospace);\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\n font-size: 1em;\n}\nsmall {\n font-size: 80%;\n}\nsub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\ntable {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n}\n:-moz-focusring:where(:not(iframe)) {\n outline: auto;\n}\nprogress {\n vertical-align: baseline;\n}\nsummary {\n display: list-item;\n}\nol, ul, menu {\n list-style: none;\n}\nimg, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n}\nimg, video {\n max-width: 100%;\n height: auto;\n}\nbutton, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n}\n:where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n}\n:where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n}\n::file-selector-button {\n margin-inline-end: 4px;\n}\n::placeholder {\n opacity: 1;\n}\n@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n}\ntextarea {\n resize: vertical;\n}\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n}\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n}\n::-webkit-calendar-picker-indicator {\n line-height: 1;\n}\n:-moz-ui-invalid {\n box-shadow: none;\n}\nbutton, input:where([type=\'button\'], [type=\'reset\'], [type=\'submit\']), ::file-selector-button {\n appearance: button;\n}\n::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n}\n[hidden]:where(:not([hidden=\'until-found\'])) {\n display: none !important;\n}\n.pointer-events-auto {\n pointer-events: auto;\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\n}\n.absolute {\n position: absolute;\n}\n.fixed {\n position: fixed;\n}\n.static {\n position: static;\n}\n.inset-0 {\n inset: 0px;\n}\n.top-1\\/2 {\n top: calc(1 / 2 * 100%);\n}\n.top-2\\.5 {\n top: calc(var(--spacing) * 2.5);\n}\n.right-2 {\n right: calc(var(--spacing) * 2);\n}\n.left-1\\/2 {\n left: calc(1 / 2 * 100%);\n}\n.z-1 {\n z-index: 1;\n}\n.block {\n display: block;\n}\n.flex {\n display: flex;\n}\n.grid {\n display: grid;\n}\n.hidden {\n display: none;\n}\n.inline-flex {\n display: inline-flex;\n}\n.h-7 {\n height: calc(var(--spacing) * 7);\n}\n.h-8 {\n height: calc(var(--spacing) * 8);\n}\n.max-h-24 {\n max-height: calc(var(--spacing) * 24);\n}\n.max-h-\\[calc\\(100vh-16px\\)\\] {\n max-height: calc(100vh - 16px);\n}\n.max-h-\\[min\\(176px\\,calc\\(100vh-180px\\)\\)\\] {\n max-height: min(176px, calc(100vh - 180px));\n}\n.min-h-7 {\n min-height: calc(var(--spacing) * 7);\n}\n.min-h-8 {\n min-height: calc(var(--spacing) * 8);\n}\n.w-6 {\n width: calc(var(--spacing) * 6);\n}\n.w-8 {\n width: calc(var(--spacing) * 8);\n}\n.w-\\[min\\(360px\\,calc\\(100vw-16px\\)\\)\\] {\n width: min(360px, calc(100vw - 16px));\n}\n.w-full {\n width: 100%;\n}\n.max-w-70 {\n max-width: calc(var(--spacing) * 70);\n}\n.min-w-0 {\n min-width: 0px;\n}\n.flex-1 {\n flex: 1;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n}\n.-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n}\n.cursor-grab {\n cursor: grab;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize {\n resize: both;\n}\n.resize-none {\n resize: none;\n}\n.appearance-none {\n appearance: none;\n}\n.grid-cols-\\[22px_minmax\\(0\\,1fr\\)\\] {\n grid-template-columns: 22px minmax(0,1fr);\n}\n.grid-cols-\\[82px_minmax\\(0\\,1fr\\)\\] {\n grid-template-columns: 82px minmax(0,1fr);\n}\n.flex-col {\n flex-direction: column;\n}\n.items-center {\n align-items: center;\n}\n.items-start {\n align-items: flex-start;\n}\n.justify-center {\n justify-content: center;\n}\n.gap-0\\.5 {\n gap: calc(var(--spacing) * 0.5);\n}\n.gap-1 {\n gap: var(--spacing);\n}\n.gap-2 {\n gap: calc(var(--spacing) * 2);\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-y-hidden {\n overflow-y: hidden;\n}\n.rounded-lg {\n border-radius: var(--t3-radius);\n}\n.rounded-md {\n border-radius: calc(var(--t3-radius) - 2px);\n}\n.rounded-xl {\n border-radius: calc(var(--t3-radius) + 4px);\n}\n.border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n}\n.border-0 {\n border-style: var(--tw-border-style);\n border-width: 0px;\n}\n.border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n}\n.border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 1px;\n}\n.border-border {\n border-color: var(--t3-border);\n}\n.border-input {\n border-color: var(--t3-input);\n}\n.border-primary {\n border-color: var(--t3-primary);\n}\n.border-transparent {\n border-color: transparent;\n}\n.border-b-transparent {\n border-bottom-color: transparent;\n}\n.bg-background {\n background-color: var(--t3-background);\n}\n.bg-muted {\n background-color: var(--t3-muted);\n}\n.bg-muted\\/40 {\n background-color: var(--t3-muted);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-muted) 40%, transparent);\n }\n}\n.bg-popover\\/95 {\n background-color: var(--t3-popover);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-popover) 95%, transparent);\n }\n}\n.bg-popover\\/96 {\n background-color: var(--t3-popover);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-popover) 96%, transparent);\n }\n}\n.bg-primary {\n background-color: var(--t3-primary);\n}\n.bg-primary\\/10 {\n background-color: var(--t3-primary);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--t3-primary) 10%, transparent);\n }\n}\n.bg-transparent {\n background-color: transparent;\n}\n.p-0 {\n padding: 0px;\n}\n.p-1 {\n padding: var(--spacing);\n}\n.p-2 {\n padding: calc(var(--spacing) * 2);\n}\n.px-0 {\n padding-inline: 0px;\n}\n.px-1 {\n padding-inline: var(--spacing);\n}\n.px-2 {\n padding-inline: calc(var(--spacing) * 2);\n}\n.px-2\\.5 {\n padding-inline: calc(var(--spacing) * 2.5);\n}\n.px-3 {\n padding-inline: calc(var(--spacing) * 3);\n}\n.py-1 {\n padding-block: var(--spacing);\n}\n.py-1\\.5 {\n padding-block: calc(var(--spacing) * 1.5);\n}\n.py-2 {\n padding-block: calc(var(--spacing) * 2);\n}\n.font-mono {\n font-family: var(--t3-font-mono);\n}\n.font-sans {\n font-family: var(--t3-font-sans);\n}\n.text-lg {\n font-size: var(--text-lg);\n line-height: var(--tw-leading, var(--text-lg--line-height));\n}\n.text-sm {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n}\n.text-xs {\n font-size: var(--text-xs);\n line-height: var(--tw-leading, var(--text-xs--line-height));\n}\n.leading-5 {\n --tw-leading: calc(var(--spacing) * 5);\n line-height: calc(var(--spacing) * 5);\n}\n.font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n}\n.font-medium {\n --tw-font-weight: var(--font-weight-medium);\n font-weight: var(--font-weight-medium);\n}\n.font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n}\n.text-foreground {\n color: var(--t3-foreground);\n}\n.text-muted-foreground {\n color: var(--t3-muted-foreground);\n}\n.text-popover-foreground {\n color: var(--t3-popover-foreground);\n}\n.text-primary {\n color: var(--t3-primary);\n}\n.text-primary-foreground {\n color: var(--t3-primary-foreground);\n}\n.shadow-2xl {\n --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-lg {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-sm {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.shadow-xs {\n --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.ring-0 {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.blur {\n --tw-blur: blur(8px);\n filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);\n}\n.backdrop-blur-xl {\n --tw-backdrop-blur: blur(var(--blur-xl));\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n}\n.outline-none {\n --tw-outline-style: none;\n outline-style: none;\n}\n.select-none {\n -webkit-user-select: none;\n user-select: none;\n}\n.placeholder\\:text-muted-foreground::placeholder {\n color: var(--t3-muted-foreground);\n}\n@media (hover: hover) {\n .hover\\:bg-accent:hover {\n background-color: var(--t3-accent);\n }\n .hover\\:bg-primary\\/90:hover {\n background-color: var(--t3-primary);\n }\n @supports (color: color-mix(in lab, red, red)) {\n .hover\\:bg-primary\\/90:hover {\n background-color: color-mix(in oklab, var(--t3-primary) 90%, transparent);\n }\n }\n .hover\\:text-accent-foreground:hover {\n color: var(--t3-accent-foreground);\n }\n}\n.focus\\:border-b-primary:focus {\n border-bottom-color: var(--t3-primary);\n}\n.focus\\:ring-0:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n.focus\\:outline-none:focus {\n --tw-outline-style: none;\n outline-style: none;\n}\n.disabled\\:pointer-events-none:disabled {\n pointer-events: none;\n}\n.disabled\\:opacity-60:disabled {\n opacity: 60%;\n}\n:host {\n --t3-font-sans: "DM Sans Variable", "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,\n sans-serif;\n --t3-font-mono: "SF Mono", "SFMono-Regular", "JetBrains Mono", Consolas, "Liberation Mono", Menlo, monospace;\n --t3-radius: 0.625rem;\n --t3-background: white;\n --t3-foreground: oklch(0.269 0 0);\n --t3-popover: white;\n --t3-popover-foreground: oklch(0.269 0 0);\n --t3-primary: oklch(0.488 0.217 264);\n --t3-primary-foreground: white;\n --t3-muted: rgb(0 0 0 / 4%);\n --t3-muted-foreground: oklch(0.556 0 0);\n --t3-accent: rgb(0 0 0 / 4%);\n --t3-accent-foreground: oklch(0.269 0 0);\n --t3-border: rgb(0 0 0 / 8%);\n --t3-input: rgb(0 0 0 / 10%);\n --t3-ring: oklch(0.488 0.217 264);\n color: var(--t3-foreground);\n font-family: var(--t3-font-sans);\n}\n* {\n box-sizing: border-box;\n border-color: var(--t3-border);\n}\nbutton, input, select, textarea {\n font: inherit;\n}\nbutton:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {\n outline: 2px solid var(--t3-ring);\n @supports (color: color-mix(in lab, red, red)) {\n outline: 2px solid color-mix(in srgb, var(--t3-ring) 72%, transparent);\n }\n outline-offset: -2px;\n}\n@property --tw-translate-x {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-leading {\n syntax: "*";\n inherits: false;\n}\n@property --tw-font-weight {\n syntax: "*";\n inherits: false;\n}\n@property --tw-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: "<length>";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: "*";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-blur {\n syntax: "*";\n inherits: false;\n}\n@property --tw-brightness {\n syntax: "*";\n inherits: false;\n}\n@property --tw-contrast {\n syntax: "*";\n inherits: false;\n}\n@property --tw-grayscale {\n syntax: "*";\n inherits: false;\n}\n@property --tw-hue-rotate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-invert {\n syntax: "*";\n inherits: false;\n}\n@property --tw-opacity {\n syntax: "*";\n inherits: false;\n}\n@property --tw-saturate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-sepia {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-drop-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-drop-shadow-size {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-blur {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-brightness {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-contrast {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-grayscale {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-hue-rotate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-invert {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-opacity {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-saturate {\n syntax: "*";\n inherits: false;\n}\n@property --tw-backdrop-sepia {\n syntax: "*";\n inherits: false;\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-border-style: solid;\n --tw-leading: initial;\n --tw-font-weight: initial;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-color: initial;\n --tw-shadow-alpha: 100%;\n --tw-inset-shadow: 0 0 #0000;\n --tw-inset-shadow-color: initial;\n --tw-inset-shadow-alpha: 100%;\n --tw-ring-color: initial;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-color: initial;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-inset: initial;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-blur: initial;\n --tw-brightness: initial;\n --tw-contrast: initial;\n --tw-grayscale: initial;\n --tw-hue-rotate: initial;\n --tw-invert: initial;\n --tw-opacity: initial;\n --tw-saturate: initial;\n --tw-sepia: initial;\n --tw-drop-shadow: initial;\n --tw-drop-shadow-color: initial;\n --tw-drop-shadow-alpha: 100%;\n --tw-drop-shadow-size: initial;\n --tw-backdrop-blur: initial;\n --tw-backdrop-brightness: initial;\n --tw-backdrop-contrast: initial;\n --tw-backdrop-grayscale: initial;\n --tw-backdrop-hue-rotate: initial;\n --tw-backdrop-invert: initial;\n --tw-backdrop-opacity: initial;\n --tw-backdrop-saturate: initial;\n --tw-backdrop-sepia: initial;\n }\n }\n}\n';
apps/desktop/src/preview/PickPreload.ts+46-6
......@@ -316,12 +316,43 @@ function createBox(color: string, fill: string): HTMLDivElement {
316316}
317317
318318function positionBox(node: HTMLElement, rect: PreviewAnnotationRect): void {
319 if (rect.width <= 0 || rect.height <= 0) {
320 node.style.display = "none";
321 return;
322 }
319323 node.style.display = "block";
320324 node.style.transform = `translate(${rect.x}px, ${rect.y}px)`;
321325 node.style.width = `${rect.width}px`;
322326 node.style.height = `${rect.height}px`;
323327}
324328
329/** Clamps a box to the viewport and clipping ancestors, so its border is never painted off-screen. */
330function visibleElementRect(element: Element): PreviewAnnotationRect {
331 const rect = element.getBoundingClientRect();
332 let left = Math.max(0, rect.left);
333 let top = Math.max(0, rect.top);
334 let right = Math.min(document.documentElement.clientWidth, rect.right);
335 let bottom = Math.min(document.documentElement.clientHeight, rect.bottom);
336 if (getComputedStyle(element).position !== "fixed") {
337 for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
338 if (ancestor === document.body || ancestor === document.documentElement) break;
339 const style = getComputedStyle(ancestor);
340 const bounds = ancestor.getBoundingClientRect();
341 const clipLeft = bounds.left + ancestor.clientLeft;
342 const clipTop = bounds.top + ancestor.clientTop;
343 if (style.overflowX !== "visible") {
344 left = Math.max(left, clipLeft);
345 right = Math.min(right, clipLeft + ancestor.clientWidth);
346 }
347 if (style.overflowY !== "visible") {
348 top = Math.max(top, clipTop);
349 bottom = Math.min(bottom, clipTop + ancestor.clientHeight);
350 }
351 }
352 }
353 return { x: left, y: top, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
354}
355
325356function createLabel(): HTMLDivElement {
326357 const label = document.createElement("div");
327358 label.setAttribute(OVERLAY_ATTRIBUTE, "");
......@@ -343,11 +374,15 @@ function updateSelectedVisual(target: SelectedElement): void {
343374 target.label.style.display = "none";
344375 return;
345376 }
346 const rect = target.element.getBoundingClientRect();
347 positionBox(target.outline, rectFromDomRect(rect));
377 const rect = visibleElementRect(target.element);
378 positionBox(target.outline, rect);
379 if (rect.width === 0 || rect.height === 0) {
380 target.label.style.display = "none";
381 return;
382 }
348383 target.label.textContent = describeRawElement(target.element);
349384 target.label.style.display = "block";
350 target.label.style.transform = `translate(${Math.max(4, rect.left)}px, ${Math.max(4, rect.top - 22)}px)`;
385 target.label.style.transform = `translate(${Math.max(4, rect.x)}px, ${Math.max(4, rect.y - 22)}px)`;
351386}
352387
353388function toStackFrame(frame: {
......@@ -995,8 +1030,9 @@ function startAnnotation(sendEnabled: boolean): void {
9951030 const getAnnotationBounds = (): PreviewAnnotationRect | null =>
9961031 unionRects(
9971032 [
998 ...Array.from(selected.values(), (target) =>
999 rectFromDomRect(target.element.getBoundingClientRect()),
1033 // Follow the visible outline, so a clipped selection does not anchor the editor off-screen.
1034 ...Array.from(selected.values(), (target) => visibleElementRect(target.element)).filter(
1035 (rect) => rect.width > 0 && rect.height > 0,
10001036 ),
10011037 ...regions.map((region) => region.rect),
10021038 ...strokes.map((stroke) => stroke.bounds),
......@@ -1108,6 +1144,8 @@ function startAnnotation(sendEnabled: boolean): void {
11081144 for (const target of selected.values()) updateSelectedVisual(target);
11091145 queueEditorLayout();
11101146 };
1147 // A scrollbar appearing narrows the viewport without a window resize.
1148 const rootResizeObserver = new ResizeObserver(repaint);
11111149
11121150 const removeTargetAtPoint = (x: number, y: number): boolean => {
11131151 for (const target of Array.from(selected.values()).toReversed()) {
......@@ -1192,7 +1230,7 @@ function startAnnotation(sendEnabled: boolean): void {
11921230 }
11931231 if (tool === "select" && dragStart === null) {
11941232 const target = pickFromPoint(event.clientX, event.clientY);
1195 if (target) positionBox(hoverOutline, rectFromDomRect(target.getBoundingClientRect()));
1233 if (target) positionBox(hoverOutline, visibleElementRect(target));
11961234 else clearHoverOutline();
11971235 return;
11981236 }
......@@ -1310,6 +1348,7 @@ function startAnnotation(sendEnabled: boolean): void {
13101348 const teardown = (notifyMain: boolean): void => {
13111349 if (finished) return;
13121350 finished = true;
1351 rootResizeObserver.disconnect();
13131352 restoreStyles();
13141353 window.removeEventListener("pointermove", onPointerMove, true);
13151354 window.removeEventListener("pointerdown", onPointerDown, true);
......@@ -1445,6 +1484,7 @@ function startAnnotation(sendEnabled: boolean): void {
14451484 ipcRenderer.on(CANCEL_PICK_CHANNEL, onCancel);
14461485 ipcRenderer.on(ANNOTATION_CAPTURED_CHANNEL, onCaptured);
14471486 document.documentElement.appendChild(host);
1487 rootResizeObserver.observe(document.documentElement);
14481488 refreshToolButtons();
14491489 updateStatus();
14501490 activeSession = {
apps/server/src/htmlRender/HtmlRender.test.ts+4-1
......@@ -244,10 +244,12 @@ describe("HtmlRender", () => {
244244 const htmlRender = yield* HtmlRender.HtmlRender;
245245 const preview = yield* htmlRender.preview({
246246 html: [
247 '<!doctype html><html><head></head><body><div style="height:300px;background:var(--accent)"></div>',
247 '<!doctype html><html><head></head><body><div id="page" tabindex="0" style="height:300px;background:var(--accent)"></div>',
248248 '<img src="/nonexistent/t3-missing.png" hidden>',
249249 "<script>",
250250 "const root = getComputedStyle(document.documentElement);",
251 'const page = document.getElementById("page"); page.focus();',
252 'const focus = getComputedStyle(page); console.log("focus", focus.outlineWidth, focus.outlineOffset, page.getBoundingClientRect().width, document.documentElement.scrollWidth);',
251253 'console.log("ready", 3); console.info(root.getPropertyValue("--font-sans"));',
252254 'console.warn(root.getPropertyValue("--background")); console.error("boom");',
253255 "</script>",
......@@ -271,6 +273,7 @@ describe("HtmlRender", () => {
271273 expect(preview.consoleMessages).toEqual(
272274 expect.arrayContaining([
273275 { level: "log", text: "ready 3" },
276 { level: "log", text: "focus 2px -2px 400 400" },
274277 { level: "info", text: HTML_RENDER_MEASURE_FONTS.sans },
275278 {
276279 level: "warning",
apps/web/src/components/ContextChip.tsx+2-2
......@@ -23,7 +23,7 @@ import { cn } from "~/lib/utils";
2323 * span with tabIndex for a tooltip) gets the focus outline.
2424 */
2525const contextChipVariants = cva(
26 "inline-flex h-[1.41em] max-w-full items-center gap-[0.33em] rounded-[0.5em] border px-[0.5em] align-middle font-medium text-[0.86em] leading-none [&_svg]:block [&_svg]:size-[1.17em] [&_svg]:shrink-0 [&_svg]:self-center [button&,a&,[data-popup-open]&]:cursor-pointer [button&,a&]:transition-colors [button&,a&]:motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground disabled:cursor-default",
26 "inline-flex h-[1.41em] max-w-full items-center gap-[0.33em] rounded-[0.5em] border px-[0.5em] align-middle font-medium text-[0.86em] leading-none [&_svg]:block [&_svg]:size-[1.17em] [&_svg]:shrink-0 [&_svg]:self-center [button&,a&,[data-popup-open]&]:cursor-pointer [button&,a&]:transition-colors [button&,a&]:motion-reduce:transition-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-foreground disabled:cursor-default",
2727 {
2828 defaultVariants: { kind: "neutral" },
2929 variants: {
......@@ -117,7 +117,7 @@ function ContextChipLabel({ className, ...props }: React.ComponentProps<"span">)
117117function ContextChipAction({ className, render, ...props }: useRender.ComponentProps<"button">) {
118118 const defaultProps = {
119119 className: cn(
120 "ml-[0.17em] inline-flex size-[1.17em] shrink-0 cursor-pointer items-center justify-center rounded-sm text-current transition-colors hover:bg-(--context-chip-accent,var(--color-foreground))/17 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring motion-reduce:transition-none [&_svg]:size-[0.85em]",
120 "ml-[0.17em] inline-flex size-[1.17em] shrink-0 cursor-pointer items-center justify-center rounded-sm text-current transition-colors hover:bg-(--context-chip-accent,var(--color-foreground))/17 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring motion-reduce:transition-none [&_svg]:size-[0.85em]",
121121 className,
122122 ),
123123 "data-slot": "context-chip-action",
apps/web/src/components/GitActionsControl.tsx+5-5
......@@ -793,9 +793,9 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {
793793 value={option.value}
794794 className={cn(
795795 "relative flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-3 text-left outline-none transition-[background-color,border-color,box-shadow]",
796 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
796 "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
797797 isSelected
798 ? "border-primary bg-background shadow-sm ring-2 ring-primary/35 dark:border-transparent dark:bg-primary/10 dark:shadow-none dark:ring-1 dark:ring-primary/30"
798 ? "border-primary bg-background shadow-sm ring-2 ring-inset ring-primary/35 dark:border-transparent dark:bg-primary/10 dark:shadow-none dark:ring-1 dark:ring-primary/30"
799799 : "border-border bg-background hover:border-foreground/20 hover:bg-muted/50 dark:border-transparent dark:bg-white/[0.035] dark:hover:bg-accent",
800800 )}
801801 >
......@@ -817,7 +817,7 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {
817817 >
818818 Repository
819819 </label>
820 <div className="flex items-stretch overflow-hidden rounded-md border border-input bg-background focus-within:outline-2 focus-within:-outline-offset-1 focus-within:outline-ring">
820 <div className="flex items-stretch overflow-hidden rounded-md border border-input bg-background focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-ring">
821821 <span className="flex shrink-0 items-center gap-1.5 border-r border-input bg-muted/50 px-2.5 font-mono text-xs text-muted-foreground">
822822 <currentPublishProvider.Icon className="size-3.5" />
823823 {publishHost}/
......@@ -879,9 +879,9 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {
879879 value={option.value}
880880 className={cn(
881881 "relative flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 text-left outline-none transition-[background-color,border-color,box-shadow]",
882 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
882 "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
883883 isSelected
884 ? "border-primary bg-background shadow-sm ring-2 ring-primary/35 dark:border-transparent dark:bg-primary/10 dark:shadow-none dark:ring-1 dark:ring-primary/30"
884 ? "border-primary bg-background shadow-sm ring-2 ring-inset ring-primary/35 dark:border-transparent dark:bg-primary/10 dark:shadow-none dark:ring-1 dark:ring-primary/30"
885885 : "border-border bg-background hover:border-foreground/20 hover:bg-muted/50 dark:border-transparent dark:bg-white/[0.035] dark:hover:bg-accent",
886886 )}
887887 >
apps/web/src/components/LegacySidebar.tsx+5-5
......@@ -246,7 +246,7 @@ const PROJECT_GROUPING_MODE_LABELS: Record<SidebarProjectGroupingMode, string> =
246246 separate: "Keep separate",
247247};
248248const SIDEBAR_ICON_ACTION_BUTTON_CLASS =
249 "inline-flex h-6 min-w-6 cursor-pointer items-center justify-center rounded-md px-0.75 text-icon-muted hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring";
249 "inline-flex h-6 min-w-6 cursor-pointer items-center justify-center rounded-md px-0.75 text-icon-muted hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring";
250250
251251function SidebarThreadDetailPrewarmer({ threadRef }: { readonly threadRef: ScopedThreadRef }) {
252252 useEnvironmentThread(threadRef.environmentId, threadRef.threadId);
......@@ -769,7 +769,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
769769 target="_blank"
770770 rel="noopener noreferrer"
771771 aria-label={prStatus.tooltip}
772 className={`inline-flex items-center justify-center ${prStatus.colorClass} cursor-pointer rounded-sm outline-hidden focus-visible:ring-1 focus-visible:ring-ring`}
772 className={`inline-flex items-center justify-center ${prStatus.colorClass} cursor-pointer rounded-sm outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring`}
773773 onPointerDown={(event) => event.stopPropagation()}
774774 onClick={handlePrClick}
775775 >
......@@ -835,7 +835,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
835835 <button
836836 type="button"
837837 aria-label={`Open localhost:${discoveredPorts[0]?.port ?? ""}`}
838 className="inline-flex cursor-pointer items-center justify-center text-success-foreground outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
838 className="inline-flex cursor-pointer items-center justify-center text-success-foreground outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
839839 onClick={handleOpenDiscoveredPort}
840840 />
841841 }
......@@ -880,7 +880,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
880880 data-thread-selection-safe
881881 data-testid={`thread-archive-confirm-${thread.id}`}
882882 aria-label={`Confirm archive ${thread.title}`}
883 className="absolute top-1/2 right-1 inline-flex h-5 -translate-y-1/2 cursor-pointer items-center rounded-md bg-destructive/12 px-2 text-3xs font-medium text-destructive transition-colors hover:bg-destructive/18 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-destructive/40"
883 className="absolute top-1/2 right-1 inline-flex h-5 -translate-y-1/2 cursor-pointer items-center rounded-md bg-destructive/12 px-2 text-3xs font-medium text-destructive transition-colors hover:bg-destructive/18 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-destructive/40"
884884 onPointerDown={stopPropagationOnPointerDown}
885885 onClick={handleConfirmArchiveClick}
886886 >
......@@ -2955,7 +2955,7 @@ function SortableProjectItem({
29552955 }}
29562956 className={`group/menu-item relative rounded-md ${
29572957 isDragging ? "z-20 opacity-80" : ""
2958 } ${isOver && !isDragging ? "ring-1 ring-primary/40" : ""}`}
2958 } ${isOver && !isDragging ? "ring-1 ring-inset ring-primary/40" : ""}`}
29592959 data-sidebar="menu-item"
29602960 data-slot="sidebar-menu-item"
29612961 >
apps/web/src/components/RightPanelTabs.tsx+1-1
......@@ -1193,7 +1193,7 @@ export function RightPanelTabs(props: RightPanelTabsProps) {
11931193 {renamingDevice === surface.id ? (
11941194 <input
11951195 aria-label="Device tab name"
1196 className="w-24 min-w-0 rounded-sm bg-background px-1 outline-none ring-1 ring-ring"
1196 className="w-24 min-w-0 rounded-sm bg-background px-1 outline-none ring-1 ring-inset ring-ring"
11971197 defaultValue={title}
11981198 ref={(element) => {
11991199 element?.focus();
apps/web/src/components/Sidebar.tsx+3-3
......@@ -1763,7 +1763,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
17631763 type="button"
17641764 aria-label="Unpin thread"
17651765 onClick={handleUnpinClick}
1766 className="group/unpin inline-flex cursor-pointer items-center rounded-sm text-muted-foreground/65 outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
1766 className="group/unpin inline-flex cursor-pointer items-center rounded-sm text-muted-foreground/65 outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
17671767 />
17681768 }
17691769 >
......@@ -1876,7 +1876,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
18761876 type="button"
18771877 aria-label="Dismiss Woke notification"
18781878 onClick={handleAcknowledgeWokeClick}
1879 className="inline-flex cursor-pointer items-center gap-1 rounded-sm text-xs font-medium text-warning-foreground outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring"
1879 className="inline-flex cursor-pointer items-center gap-1 rounded-sm text-xs font-medium text-warning-foreground outline-none hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
18801880 >
18811881 <AlarmClockIcon aria-hidden className="size-3" />
18821882 <span role="status">Woke</span>
......@@ -2040,7 +2040,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
20402040 aria-label="Dismiss Woke notification"
20412041 onClick={handleAcknowledgeWokeClick}
20422042 className={cn(
2043 "inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring",
2043 "inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium outline-none hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
20442044 topStatus.className,
20452045 )}
20462046 >
apps/web/src/components/auth/ConnectAgentSurface.tsx+2-2
......@@ -311,9 +311,9 @@ function AccessOption({
311311 value={access}
312312 className={cn(
313313 "flex cursor-pointer items-start gap-3 rounded-lg border px-3 py-2.5 text-left outline-none transition-[background-color,border-color,box-shadow]",
314 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
314 "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
315315 selected
316 ? "border-primary bg-background ring-2 ring-primary/25 dark:border-transparent dark:bg-primary/10 dark:ring-1 dark:ring-primary/30"
316 ? "border-primary bg-background ring-2 ring-inset ring-primary/25 dark:border-transparent dark:bg-primary/10 dark:ring-1 dark:ring-primary/30"
317317 : "border-border bg-background hover:bg-muted/50 dark:border-transparent dark:bg-white/[0.035] dark:hover:bg-accent",
318318 )}
319319 >
apps/web/src/components/chat/ChangedFilesTree.tsx+2-2
......@@ -178,7 +178,7 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: {
178178 type="button"
179179 data-scroll-anchor-ignore
180180 aria-expanded={isExpanded}
181 className="group flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
181 className="group flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
182182 style={{ paddingLeft: `${leftPadding}px` }}
183183 onClick={() => toggleDirectory(node.path)}
184184 >
......@@ -213,7 +213,7 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: {
213213 <button
214214 key={`file:${node.path}`}
215215 type="button"
216 className="group flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
216 className="group flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
217217 style={{ paddingLeft: `${leftPadding}px` }}
218218 onClick={() => onOpenTurnDiff(runId, node.path)}
219219 onContextMenu={
apps/web/src/components/chat/ChatComposer.tsx+3-3
......@@ -5314,7 +5314,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
53145314 {restingImagePreviewCounts.overflowCount > 0 ? (
53155315 <button
53165316 type="button"
5317 className="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-border/70 bg-muted/60 font-medium text-secondary-label text-xs tabular-nums outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
5317 className="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-border/70 bg-muted/60 font-medium text-secondary-label text-xs tabular-nums outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
53185318 aria-label={`Show ${String(restingImagePreviewCounts.overflowCount)} more image attachments`}
53195319 onPointerDown={(event) => event.preventDefault()}
53205320 onClick={() => {
......@@ -6947,8 +6947,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
69476947 data-chat-composer-mobile-collapsed={isComposerCollapsedMobile ? "true" : "false"}
69486948 className={cn(
69496949 "rounded-3xl transition-[background-color] duration-200",
6950 "in-data-[thread-context-over]:bg-accent/45 in-data-[thread-context-over]:ring-1 in-data-[thread-context-over]:ring-primary/70",
6951 isDragOverComposer ? "bg-accent/45 ring-1 ring-primary/70" : null,
6950 "in-data-[thread-context-over]:bg-accent/45 in-data-[thread-context-over]:ring-1 in-data-[thread-context-over]:ring-inset in-data-[thread-context-over]:ring-primary/70",
6951 isDragOverComposer ? "bg-accent/45 ring-1 ring-inset ring-primary/70" : null,
69526952 projectSelectionRequired ? "opacity-75" : null,
69536953 composerProviderState.composerSurfaceClassName,
69546954 )}
apps/web/src/components/chat/ChatHeader.tsx+4-4
......@@ -323,7 +323,7 @@ export const ChatHeader = memo(function ChatHeader({
323323 type="button"
324324 aria-label={`New thread in ${activeProjectName}`}
325325 onClick={onNewThreadInProject}
326 className="inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1.5 rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
326 className="inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1.5 rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
327327 />
328328 }
329329 >
......@@ -350,7 +350,7 @@ export const ChatHeader = memo(function ChatHeader({
350350 type="button"
351351 aria-label={`Open parent thread: ${parentThreadLink.title}`}
352352 onClick={() => onOpenThread(parentThreadLink.threadId)}
353 className="inline-flex min-w-0 max-w-full cursor-pointer items-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
353 className="inline-flex min-w-0 max-w-full cursor-pointer items-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
354354 />
355355 }
356356 >
......@@ -379,7 +379,7 @@ export const ChatHeader = memo(function ChatHeader({
379379 <input
380380 autoFocus
381381 aria-label="Thread title"
382 className="min-w-0 flex-1 rounded-sm bg-transparent text-sm font-medium text-foreground outline-none ring-1 ring-ring/50 focus:ring-ring"
382 className="min-w-0 flex-1 rounded-sm bg-transparent text-sm font-medium text-foreground outline-none ring-1 ring-inset ring-ring/50 focus:ring-ring"
383383 defaultValue={renamingTitle}
384384 onBlur={(event) => {
385385 if (renameCommittedRef.current) return;
......@@ -409,7 +409,7 @@ export const ChatHeader = memo(function ChatHeader({
409409 onClick={openMenuFromTitle}
410410 onDoubleClick={canOperateThread ? handleTitleDoubleClick : undefined}
411411 onBlur={cancelPendingTitleMenu}
412 className="group/thread-title inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1 rounded-sm text-left focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
412 className="group/thread-title inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1 rounded-sm text-left focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
413413 />
414414 }
415415 >
apps/web/src/components/chat/ComposerControl.tsx+1-1
......@@ -16,7 +16,7 @@ export type ComposerControlSize = "sm" | "xs";
1616 */
1717function composerControlClassName(size: ComposerControlSize, className?: string) {
1818 return cn(
19 "relative inline-flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap rounded-(--control-radius) border border-transparent text-base outline-none hover:bg-accent data-pressed:bg-accent focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-disabled:pointer-events-none data-disabled:opacity-64 pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&:active:not([aria-haspopup])]:scale-[0.97] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:-mx-0.5 [&_svg[data-composer-control-icon]]:mx-0",
19 "relative inline-flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap rounded-(--control-radius) border border-transparent text-base outline-none hover:bg-accent data-pressed:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-disabled:pointer-events-none data-disabled:opacity-64 pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&:active:not([aria-haspopup])]:scale-[0.97] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:-mx-0.5 [&_svg[data-composer-control-icon]]:mx-0",
2020 size === "xs"
2121 ? "h-7 gap-1 px-1.75 font-normal text-muted-foreground/70 text-sm hover:text-foreground/80 sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5 [&_svg[data-composer-control-chevron]]:ms-0 [&_svg[data-composer-control-chevron]]:-me-1"
2222 : "h-7 gap-1.5 px-2.5 font-medium text-secondary-label [&_svg:not([class*='text-'])]:text-muted-foreground hover:text-foreground sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx+1-1
......@@ -261,7 +261,7 @@ const ComposerPendingUserInputCard = memo(function ComposerPendingUserInputCard(
261261 (!customAnswerActive && progress.selectedOptionValues.includes(optionValue));
262262 const shortcutKey = index < 9 ? index + 1 : null;
263263 const className = cn(
264 "group flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left outline-none transition-colors duration-150 focus-visible:ring-1 focus-visible:ring-primary/25",
264 "group flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left outline-none transition-colors duration-150 focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-primary/25",
265265 isSelected
266266 ? "bg-muted/55 text-foreground"
267267 : "bg-transparent text-foreground/85 hover:bg-muted/30",
apps/web/src/components/chat/ComposerPrimaryActions.tsx+2-2
......@@ -79,7 +79,7 @@ const formatPendingPrimaryActionLabel = (input: {
7979// The composer's labeled primary actions (Submit, Refine, Implement) share the send button's
8080// message-action pill, so they are composer-owned buttons rather than restyled Buttons.
8181const messageActionPillClassName =
82 "inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-full bg-message-action font-medium text-base text-message-action-foreground shadow-xs shadow-message-action/24 outline-none hover:bg-message-action-hover focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 disabled:shadow-none sm:text-sm";
82 "inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-full bg-message-action font-medium text-base text-message-action-foreground shadow-xs shadow-message-action/24 outline-none hover:bg-message-action-hover focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 disabled:shadow-none sm:text-sm";
8383
8484const preventPointerFocus: PointerEventHandler<HTMLElement> = (event) => {
8585 event.preventDefault();
......@@ -370,7 +370,7 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
370370 <button
371371 type="button"
372372 className={cn(
373 "flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 [&_svg]:pointer-events-none [&_svg]:size-3.5",
373 "flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 [&_svg]:pointer-events-none [&_svg]:size-3.5",
374374 keepFullHistory
375375 ? "border-border text-muted-foreground hover:text-foreground"
376376 : "border-warning/40 text-warning hover:bg-warning/8",
apps/web/src/components/chat/ComposerServerUpdateStatus.tsx+1-1
......@@ -47,7 +47,7 @@ export function ComposerServerUpdateStatus({
4747 <button
4848 type="button"
4949 aria-label={`${title}: ${detail}`}
50 className="block max-w-full cursor-help truncate rounded-sm text-left outline-none focus-visible:ring-2 focus-visible:ring-ring"
50 className="block max-w-full cursor-help truncate rounded-sm text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
5151 onClick={() => setDetailsOpen(true)}
5252 >
5353 {title}
apps/web/src/components/chat/ComposerStashMenu.tsx+1-1
......@@ -164,7 +164,7 @@ export const ComposerStashMenu = memo(function ComposerStashMenu(props: {
164164 <ComposerBanner.Content>
165165 <button
166166 type="button"
167 className="min-w-0 flex-1 cursor-pointer truncate text-left text-foreground/80 outline-none before:absolute before:inset-0 before:rounded-sm focus-visible:before:ring-2 focus-visible:before:ring-ring"
167 className="min-w-0 flex-1 cursor-pointer truncate text-left text-foreground/80 outline-none before:absolute before:inset-0 before:rounded-sm focus-visible:before:ring-2 focus-visible:before:ring-inset focus-visible:before:ring-ring"
168168 data-stash-restore={entry.id}
169169 aria-label={`Restore stashed prompt: ${stashEntrySnippet(entry)}`}
170170 onPointerDown={(event) => event.preventDefault()}
apps/web/src/components/chat/DraftHeroHeadline.tsx+1-1
......@@ -312,7 +312,7 @@ export function DraftHeroHeadline({
312312 <button
313313 type="button"
314314 onClick={openAddProject}
315 className="pointer-events-auto inline cursor-pointer border-muted-foreground/35 border-b border-dotted text-muted-foreground/60 transition-colors hover:border-muted-foreground/60 hover:text-muted-foreground/80 focus-visible:rounded-sm focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
315 className="pointer-events-auto inline cursor-pointer border-muted-foreground/35 border-b border-dotted text-muted-foreground/60 transition-colors hover:border-muted-foreground/60 hover:text-muted-foreground/80 focus-visible:rounded-sm focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
316316 >
317317 {activeProjectTitle ?? "Add a project"}
318318 </button>
apps/web/src/components/chat/MessagesTimeline.tsx+3-3
......@@ -1817,7 +1817,7 @@ function TimelineMinimap({
18171817 />
18181818 <button
18191819 aria-label={`Jump to message: ${activeItem?.userText ?? "User message"}`}
1820 className="absolute inset-y-0 left-0 w-full cursor-pointer bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/70"
1820 className="absolute inset-y-0 left-0 w-full cursor-pointer bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70"
18211821 onBlur={() => setActiveIndex(null)}
18221822 onClick={(event) => {
18231823 if (timelineMinimapEventTargetsPreview(event.target)) {
......@@ -2457,7 +2457,7 @@ function UserTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "message"
24572457 type="button"
24582458 aria-label={`Preview ${file.name}`}
24592459 onClick={() => ctx.onFileOpen(file)}
2460 className="focus-visible:ring-ring/70 flex min-w-0 flex-1 cursor-pointer items-center gap-2 rounded-md py-1 text-left text-sm hover:underline focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset"
2460 className="focus-visible:ring-ring/70 flex min-w-0 flex-1 cursor-pointer items-center gap-2 rounded-md py-1 text-left text-sm hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:outline-none"
24612461 >
24622462 {fileIdentity}
24632463 <EyeIcon className="size-4 shrink-0" />
......@@ -5674,7 +5674,7 @@ function WorkEntryLogRow(props: WorkEntryRowProps) {
56745674 {createdThread ? (
56755675 <button
56765676 type="button"
5677 className="shrink-0 rounded-sm text-sm text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
5677 className="shrink-0 rounded-sm text-sm text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
56785678 aria-label={`Open ${createdThread.title ?? "created thread"}`}
56795679 onClick={(event) => {
56805680 event.stopPropagation();
apps/web/src/components/chat/ThreadDetailsControl.tsx+1-1
......@@ -49,7 +49,7 @@ export function ThreadDetailsControl({
4949 {
5050 type: render ? undefined : "button",
5151 className: cn(
52 "relative inline-flex shrink-0 cursor-pointer items-center whitespace-nowrap border outline-none transition-[box-shadow,scale] [&:active:not([aria-haspopup])]:scale-[0.97] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 disabled:pointer-events-none disabled:opacity-64 aria-disabled:cursor-not-allowed aria-disabled:opacity-64 [&_svg]:shrink-0",
52 "relative inline-flex shrink-0 cursor-pointer items-center whitespace-nowrap border outline-none transition-[box-shadow,scale] [&:active:not([aria-haspopup])]:scale-[0.97] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 disabled:pointer-events-none disabled:opacity-64 aria-disabled:cursor-not-allowed aria-disabled:opacity-64 [&_svg]:shrink-0",
5353 parts[part],
5454 multiline && "h-auto min-h-8 py-0.75 disabled:opacity-100 sm:h-auto",
5555 tone === "muted" && "text-muted-foreground/70 hover:text-foreground/80",
apps/web/src/components/chat/V2LifecycleRow.tsx+1-1
......@@ -596,7 +596,7 @@ function HandoffEndpoint(props: {
596596 render={
597597 <span
598598 tabIndex={0}
599 className="inline-flex min-w-0 items-center gap-1 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
599 className="inline-flex min-w-0 items-center gap-1 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
600600 >
601601 <ProviderInstanceIcon
602602 driverKind={entry?.driverKind ?? ProviderDriverKind.make(props.instanceId)}
apps/web/src/components/chat/ZoomableImage.tsx+1-1
......@@ -150,7 +150,7 @@ export function ZoomableImage({
150150 aria-label={`${name}, zoomable image`}
151151 aria-description="Click to zoom in or return to fit. Scroll to zoom, drag to pan. Use Enter to toggle zoom, plus or minus to zoom, and 0 to fit."
152152 tabIndex={0}
153 className="max-w-[var(--media-width)] overflow-auto overscroll-contain rounded-lg bg-background shadow-2xl ring-1 ring-border/70 outline-none focus-visible:ring-2 focus-visible:ring-ring"
153 className="max-w-[var(--media-width)] overflow-auto overscroll-contain rounded-lg bg-background shadow-2xl ring-1 ring-border/70 outline-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring"
154154 style={{
155155 width: width || undefined,
156156 height: height || undefined,
apps/web/src/components/files/FileBreadcrumbs.tsx+1-1
......@@ -237,7 +237,7 @@ function DirectoryBreadcrumb(props: FileBreadcrumbsProps & { readonly crumb: Fil
237237 <button
238238 type="button"
239239 aria-label={`Browse ${props.crumb.label}`}
240 className="relative block max-w-40 cursor-pointer rounded-sm px-0.5 text-left text-muted-foreground outline-none pointer-coarse:after:-inset-y-3 pointer-coarse:after:absolute pointer-coarse:after:inset-x-0 hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring data-popup-open:bg-accent data-popup-open:text-foreground"
240 className="relative block max-w-40 cursor-pointer rounded-sm px-0.5 text-left text-muted-foreground outline-none pointer-coarse:after:-inset-y-3 pointer-coarse:after:absolute pointer-coarse:after:inset-x-0 hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-popup-open:bg-accent data-popup-open:text-foreground"
241241 />
242242 }
243243 />
apps/web/src/components/files/fileSurfaceChrome.tsx+1-1
......@@ -153,7 +153,7 @@ export function FileSurfaceFailure(props: {
153153 <button
154154 type="button"
155155 onClick={props.onRetry}
156 className="rounded-md border border-input px-2.5 py-1 text-xs text-foreground hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring"
156 className="rounded-md border border-input px-2.5 py-1 text-xs text-foreground hover:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
157157 >
158158 Try again
159159 </button>
apps/web/src/components/media/MediaVideoPlayer.tsx+1-1
......@@ -203,7 +203,7 @@ export function MediaVideoPlayer({
203203 type="button"
204204 aria-label={label ? `Play ${label}` : "Play video"}
205205 onClick={onOpen}
206 className="absolute inset-0 flex cursor-pointer items-center justify-center rounded-lg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
206 className="absolute inset-0 flex cursor-pointer items-center justify-center rounded-lg focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring"
207207 >
208208 <span className="flex size-8 items-center justify-center rounded-full bg-black/50 text-white">
209209 <PlayIcon aria-hidden className="size-4 fill-current" />
apps/web/src/components/preview/PreviewRecentUrlCard.tsx+1-1
......@@ -44,7 +44,7 @@ export function PreviewRecentUrlCard({ threadRef, entry, onOpen, onRemove }: Pro
4444 type="button"
4545 aria-label={`Remove ${label} from history`}
4646 onClick={onRemove}
47 className="absolute right-3 rounded p-1 text-muted-foreground opacity-0 hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100"
47 className="absolute right-3 rounded p-1 text-muted-foreground opacity-0 hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring group-hover:opacity-100"
4848 >
4949 <X className="size-3.5" />
5050 </button>
apps/web/src/components/projectScriptEditor.tsx+1-1
......@@ -369,7 +369,7 @@ export function ProjectScriptEditorDialog({
369369 type="button"
370370 className={`relative flex flex-col items-center gap-2 rounded-md border px-2 py-2 text-xs dark:border-transparent ${
371371 isSelected
372 ? "border-primary/70 bg-primary/10 dark:ring-1 dark:ring-primary/30"
372 ? "border-primary/70 bg-primary/10 ring-inset dark:ring-1 dark:ring-primary/30"
373373 : "border-border/70 hover:bg-accent/60 dark:bg-white/[0.035]"
374374 }`}
375375 onClick={() => {
apps/web/src/components/pullRequest/PullRequestCopyableCode.tsx+1-1
......@@ -32,7 +32,7 @@ export function PullRequestCopyableCode({
3232 <button
3333 type="button"
3434 className={cn(
35 "relative grid w-fit min-w-0 max-w-full shrink cursor-pointer rounded px-1 py-0.5 text-left outline-none transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent/45 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
35 "relative grid w-fit min-w-0 max-w-full shrink cursor-pointer rounded px-1 py-0.5 text-left outline-none transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent/45 hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
3636 className,
3737 )}
3838 aria-label={isCopied ? copiedLabel : copyLabel}
apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx+1-1
......@@ -380,7 +380,7 @@ function PullRequestBaseFreshnessWarning({
380380 type="button"
381381 aria-label={summary}
382382 className={cn(
383 "inline-flex min-w-0 shrink-0 cursor-help items-center gap-1 rounded-sm text-warning-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring",
383 "inline-flex min-w-0 shrink-0 cursor-help items-center gap-1 rounded-sm text-warning-foreground outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
384384 className,
385385 )}
386386 />
apps/web/src/components/pullRequest/PullRequestReactions.tsx+2-2
......@@ -23,7 +23,7 @@ import {
2323} from "./pullRequestReactions.logic";
2424
2525const PILL_CLASS =
26 "inline-flex h-6 shrink-0 items-center gap-1 rounded-full border px-2 text-xs outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring";
26 "inline-flex h-6 shrink-0 items-center gap-1 rounded-full border px-2 text-xs outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring";
2727
2828const EMPTY_PENDING: ReadonlyMap<PullRequestReactionContent, boolean> = new Map();
2929
......@@ -148,7 +148,7 @@ export function PullRequestReactionBar({
148148 aria-pressed={reacted}
149149 aria-label={pullRequestReactionName(content)}
150150 className={cn(
151 "flex size-7 items-center justify-center rounded-md text-base outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring",
151 "flex size-7 items-center justify-center rounded-md text-base outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
152152 reacted && "bg-primary/10",
153153 )}
154154 onClick={() => {
apps/web/src/components/pullRequest/PullRequestRow.tsx+1-1
......@@ -136,7 +136,7 @@ function PullRequestRowImpl({
136136 className={cn(
137137 PULL_REQUEST_ROW_CLASS,
138138 PAGE_ROW_CLASS,
139 "min-w-0 flex-1 cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
139 "min-w-0 flex-1 cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
140140 )}
141141 >
142142 <PullRequestRowGlyph
apps/web/src/components/pullRequest/PullRequestTimelineTab.tsx+1-1
......@@ -376,7 +376,7 @@ function CommitEvent({
376376 return (
377377 <button
378378 type="button"
379 className="group relative mb-5 block w-full cursor-pointer rounded-sm pl-12 text-left outline-none [contain-intrinsic-block-size:48px] [content-visibility:auto] focus-visible:ring-2 focus-visible:ring-ring"
379 className="group relative mb-5 block w-full cursor-pointer rounded-sm pl-12 text-left outline-none [contain-intrinsic-block-size:48px] [content-visibility:auto] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
380380 aria-label={`View commit ${event.id}`}
381381 onClick={() => onOpen(event.id)}
382382 >
apps/web/src/components/settings/AddProviderInstanceDialog.tsx+1-1
......@@ -435,7 +435,7 @@ export function AddProviderInstanceDialog({
435435 <RadioPrimitive.Root
436436 key={option.driverKind}
437437 value={option.driverKind}
438 className="relative flex cursor-pointer items-center gap-3 rounded-lg bg-card px-3 py-3 text-left text-muted-foreground outline-none ring-1 ring-black/5 hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring data-checked:bg-primary/8 data-checked:text-foreground data-checked:ring-2 data-checked:ring-primary data-checked:hover:bg-primary/8 dark:bg-white/3 dark:ring-white/5 dark:hover:bg-white/5 dark:data-checked:bg-primary/15 dark:data-checked:ring-primary dark:data-checked:hover:bg-primary/15"
438 className="relative flex cursor-pointer items-center gap-3 rounded-lg bg-card px-3 py-3 text-left text-muted-foreground outline-none ring-1 ring-black/5 hover:bg-muted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-checked:bg-primary/8 data-checked:text-foreground data-checked:ring-2 data-checked:ring-inset data-checked:ring-primary data-checked:hover:bg-primary/8 dark:bg-white/3 dark:ring-white/5 dark:hover:bg-white/5 dark:data-checked:bg-primary/15 dark:data-checked:ring-primary dark:data-checked:hover:bg-primary/15"
439439 >
440440 <ProviderInstanceIcon
441441 driverKind={option.driverKind}
apps/web/src/components/settings/BrowserImportWizard.tsx+1-1
......@@ -457,7 +457,7 @@ function SelectableTile({
457457 aria-pressed={selected}
458458 onClick={onSelect}
459459 className={cn(
460 "flex w-full cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-2 text-left outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
460 "flex w-full cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-2 text-left outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
461461 selected
462462 ? "border-primary bg-primary/8"
463463 : "border-border/60 hover:border-border hover:bg-muted/40",
apps/web/src/components/settings/CodexSetupSection.tsx+1-1
......@@ -1098,7 +1098,7 @@ function CodexSignInDescription({
10981098 return (
10991099 <button
11001100 type="button"
1101 className="inline-flex items-center gap-1.5 rounded-sm text-left leading-relaxed outline-none focus-visible:ring-2 focus-visible:ring-ring"
1101 className="inline-flex items-center gap-1.5 rounded-sm text-left leading-relaxed outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
11021102 aria-label="Having trouble signing in?"
11031103 aria-expanded={expanded}
11041104 aria-controls={controls}
apps/web/src/components/settings/ConnectionsSettings.tsx+4-4
......@@ -870,7 +870,7 @@ const PairingLinkListRow = memo(function PairingLinkListRow({
870870 role="radio"
871871 aria-checked={isSelected}
872872 className={cn(
873 "flex w-full items-baseline gap-2 rounded-lg border px-2.5 py-1.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring",
873 "flex w-full items-baseline gap-2 rounded-lg border px-2.5 py-1.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
874874 isSelected
875875 ? "border-foreground/60 bg-muted/30"
876876 : "border-border/50 hover:bg-muted/20",
......@@ -1651,7 +1651,7 @@ function SavedBackendListRow({
16511651 type="button"
16521652 aria-expanded={routesOpen}
16531653 onClick={() => setRoutesOpen((open) => !open)}
1654 className="inline-flex shrink-0 items-center gap-0.5 rounded-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring"
1654 className="inline-flex shrink-0 items-center gap-0.5 rounded-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
16551655 >
16561656 {routeCount === 1 ? "Routes" : `${routeCount} routes`}
16571657 <ChevronRightIcon
......@@ -1670,7 +1670,7 @@ function SavedBackendListRow({
16701670 aria-expanded={permissionsOpen}
16711671 aria-controls={`remote-permissions-${environmentId}`}
16721672 onClick={() => setPermissionsOpen((open) => !open)}
1673 className="inline-flex shrink-0 items-center gap-0.5 rounded-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring"
1673 className="inline-flex shrink-0 items-center gap-0.5 rounded-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
16741674 >
16751675 Permissions
16761676 <ChevronRightIcon
......@@ -3718,7 +3718,7 @@ export function ConnectionsSettings() {
37183718 ) : null}
37193719 {primaryEnvironment ? (
37203720 <details className="group px-3 sm:px-4">
3721 <summary className="flex min-h-11 cursor-pointer list-none items-center gap-2 rounded-md text-sm font-medium outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden">
3721 <summary className="flex min-h-11 cursor-pointer list-none items-center gap-2 rounded-md text-sm font-medium outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [&::-webkit-details-marker]:hidden">
37223722 Permissions
37233723 <ChevronRightIcon
37243724 aria-hidden
apps/web/src/components/settings/EnvironmentRoutesList.tsx+1-1
......@@ -195,7 +195,7 @@ function SortableRouteRow({
195195 {...attributes}
196196 {...listeners}
197197 aria-label={`Reorder ${label}, position ${position}`}
198 className="flex size-6 shrink-0 cursor-grab touch-none items-center justify-center rounded text-muted-foreground/70 outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing"
198 className="flex size-6 shrink-0 cursor-grab touch-none items-center justify-center rounded text-muted-foreground/70 outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring active:cursor-grabbing"
199199 >
200200 <GripVerticalIcon className="size-3.5" />
201201 </button>
apps/web/src/components/settings/FoldedSettingsSection.tsx+2-2
......@@ -46,7 +46,7 @@ export function FoldedSettingsSection({
4646 className="flex min-h-7 items-start justify-between gap-4 px-3 sm:px-4"
4747 >
4848 <h2>
49 <CollapsibleTrigger className="flex min-h-7 items-center gap-2 rounded-md text-sm font-normal text-foreground/70 outline-none focus-visible:ring-2 focus-visible:ring-ring">
49 <CollapsibleTrigger className="flex min-h-7 items-center gap-2 rounded-md text-sm font-normal text-foreground/70 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
5050 {title}
5151 <ChevronRightIcon
5252 aria-hidden
......@@ -72,7 +72,7 @@ export function FoldedSettingsSection({
7272 <section id={id} ref={targetRef} tabIndex={-1} className="outline-none">
7373 <Collapsible open={open} onOpenChange={setOpen} render={<SettingsGroup divided={false} />}>
7474 <div className="flex items-center gap-4 px-3 sm:px-4">
75 <CollapsibleTrigger className="flex min-h-11 min-w-0 flex-1 items-center gap-2 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-md">
75 <CollapsibleTrigger className="flex min-h-11 min-w-0 flex-1 items-center gap-2 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring rounded-md">
7676 <ChevronRightIcon
7777 aria-hidden
7878 className={cn(
apps/web/src/components/settings/KeybindingsSettings.tsx+2-2
......@@ -230,7 +230,7 @@ function WarningTooltipIcon({
230230 tabIndex={focusable ? 0 : undefined}
231231 aria-label={label}
232232 className={cn(
233 "inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-warning outline-none transition-colors hover:bg-warning/10 focus-visible:ring-3 focus-visible:ring-warning/25",
233 "inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-warning outline-none transition-colors hover:bg-warning/10 focus-visible:ring-3 focus-visible:ring-inset focus-visible:ring-warning/25",
234234 className,
235235 )}
236236 />
......@@ -818,7 +818,7 @@ function KeybindingKeyControl({
818818 onClick={() => setDraft({ isRecording: true })}
819819 aria-label={`Edit shortcut for ${commandLabel(row.command)}: ${formatShortcutLabel(row.binding.shortcut)}`}
820820 className={cn(
821 "inline-flex h-8 cursor-pointer items-center rounded-md border border-transparent px-1.5 sm:h-7 outline-none transition-colors hover:border-border/70 hover:bg-accent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/24",
821 "inline-flex h-8 cursor-pointer items-center rounded-md border border-transparent px-1.5 sm:h-7 outline-none transition-colors hover:border-border/70 hover:bg-accent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-inset focus-visible:ring-ring/24",
822822 pillClassName,
823823 )}
824824 >
apps/web/src/components/settings/PanelAnimationsPreview.tsx+1-1
......@@ -11,7 +11,7 @@ export function PanelAnimationsPreview({ durationMs }: { durationMs: number }) {
1111 <button
1212 type="button"
1313 aria-label="Replay panel animation preview"
14 className="flex h-10 w-full cursor-pointer overflow-hidden rounded-lg border border-border bg-background p-1 shadow-xs/5 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
14 className="flex h-10 w-full cursor-pointer overflow-hidden rounded-lg border border-border bg-background p-1 shadow-xs/5 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
1515 onClick={() => setPanelsOpen((open) => !open)}
1616 style={{ "--preview-duration": `${durationMs}ms` } as CSSProperties}
1717 >
apps/web/src/components/settings/ProjectIconPickerDialog.tsx+3-3
......@@ -131,7 +131,7 @@ export function ProjectIconPickerDialog({
131131 aria-label={option.label}
132132 aria-pressed={color === option.value}
133133 className={cn(
134 "flex size-6 items-center justify-center rounded-full border border-transparent outline-none focus-visible:ring-2 focus-visible:ring-ring",
134 "flex size-6 items-center justify-center rounded-full border border-transparent outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
135135 color === option.value && "border-foreground/64",
136136 )}
137137 onClick={() => setColor(option.value)}
......@@ -161,7 +161,7 @@ export function ProjectIconPickerDialog({
161161 aria-label={iconLabel(name)}
162162 aria-pressed={iconName === name}
163163 className={cn(
164 "flex aspect-square items-center justify-center rounded-md border border-transparent outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring",
164 "flex aspect-square items-center justify-center rounded-md border border-transparent outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
165165 iconName === name && "border-border bg-accent",
166166 selectedColorClassName,
167167 )}
......@@ -211,7 +211,7 @@ export function ProjectIconPickerDialog({
211211 aria-label={option.label}
212212 aria-pressed={emoji === option.emoji}
213213 className={cn(
214 "flex aspect-square items-center justify-center rounded-md border border-transparent text-xl outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring",
214 "flex aspect-square items-center justify-center rounded-md border border-transparent text-xl outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
215215 emoji === option.emoji && "border-border bg-accent",
216216 )}
217217 onClick={() => setEmoji(option.emoji)}
apps/web/src/components/settings/ProviderInstanceCard.tsx+1-1
......@@ -923,7 +923,7 @@ export function ProviderInstanceCard({
923923 >
924924 <button
925925 type="button"
926 className="pointer-events-auto absolute inset-0 cursor-pointer rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring"
926 className="pointer-events-auto absolute inset-0 cursor-pointer rounded-md outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
927927 onClick={onSelect}
928928 aria-label={`Select ${displayName}`}
929929 aria-pressed={selected}
apps/web/src/components/settings/ThemeColorPicker.tsx+2-2
......@@ -266,7 +266,7 @@ function ThemeColorPicker({
266266 render={
267267 <button
268268 aria-label={`Choose ${label} color`}
269 className="relative flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border border-foreground/30 transition-transform hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
269 className="relative flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border border-foreground/30 transition-transform hover:scale-105 focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring"
270270 onFocus={onInteract}
271271 onPointerDown={onInteract}
272272 type="button"
......@@ -333,7 +333,7 @@ export const ThemeColorField = memo(function ThemeColorField({
333333 <button
334334 aria-label={`${selected ? "Hide" : "Show"} ${label} usage`}
335335 aria-pressed={selected}
336 className="flex min-w-0 flex-1 cursor-pointer items-center rounded-md text-left text-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
336 className="flex min-w-0 flex-1 cursor-pointer items-center rounded-md text-left text-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
337337 onClick={() => onToggleSelected?.(role)}
338338 type="button"
339339 >
apps/web/src/components/settings/ThemeImportDialog.tsx+1-1
......@@ -111,7 +111,7 @@ function ThemeJsonEditor({
111111 }, []);
112112
113113 return (
114 <div className="relative overflow-hidden rounded-xl border border-input bg-background shadow-xs/5 focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/24">
114 <div className="relative overflow-hidden rounded-xl border border-input bg-background shadow-xs/5 focus-within:border-ring focus-within:ring-3 focus-within:ring-inset focus-within:ring-ring/24">
115115 {isPlainText ? null : (
116116 <pre
117117 ref={highlightRef}
apps/web/src/components/settings/ThemePreviewCircles.tsx+1-1
......@@ -187,7 +187,7 @@ export function ThemePreviewCircles({
187187 aria-label={`Use ${label} ${mode} mode`}
188188 aria-pressed={isPicked}
189189 className={cn(
190 "relative flex size-[68px] shrink-0 transform-gpu cursor-pointer items-center justify-center rounded-full p-1 outline-none transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card",
190 "relative flex size-[68px] shrink-0 transform-gpu cursor-pointer items-center justify-center rounded-full p-1 outline-none transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card",
191191 isPicked && "hover:scale-100",
192192 )}
193193 onClick={(event) => {
apps/web/src/components/settings/ThemeSettings.tsx+9-7
......@@ -197,7 +197,7 @@ function ThemeLibraryCard({
197197 : `Use ${mode} variant, currently ${selected.option.label}`
198198 }
199199 aria-pressed={isActive}
200 className="absolute left-1/2 top-2 z-20 flex size-14 items-center justify-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring"
200 className="absolute left-1/2 top-2 z-20 flex size-14 items-center justify-center rounded-full outline-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring"
201201 style={{
202202 transform: `translateX(calc(-50% + ${rootOffsetX}px))`,
203203 }}
......@@ -216,7 +216,7 @@ function ThemeLibraryCard({
216216 {isActive ? (
217217 <span
218218 aria-hidden
219 className="pointer-events-none absolute inset-0 rounded-full ring-2 ring-ring"
219 className="pointer-events-none absolute inset-0 rounded-full ring-2 ring-inset ring-ring"
220220 />
221221 ) : null}
222222 {isActive ? (
......@@ -256,8 +256,10 @@ function ThemeLibraryCard({
256256 aria-label={`Use ${option.label} for ${mode} mode${optionIsActive ? ", currently active" : ""}`}
257257 aria-pressed={optionIsActive}
258258 className={cn(
259 "absolute left-1/2 top-1 z-30 flex size-7 items-center justify-center rounded-full bg-background shadow-sm outline-none transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none focus-visible:ring-2 focus-visible:ring-ring",
260 optionIsActive ? "ring-2 ring-ring" : "ring-1 ring-border/70",
259 "absolute left-1/2 top-1 z-30 flex size-7 items-center justify-center rounded-full bg-background shadow-sm outline-none transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
260 optionIsActive
261 ? "ring-2 ring-inset ring-ring"
262 : "ring-1 ring-border/70",
261263 )}
262264 style={{
263265 opacity: isOpen ? 1 : 0,
......@@ -303,7 +305,7 @@ function ThemeLibraryCard({
303305 <button
304306 aria-label={`Use ${variantNavigation ? `${variantNavigation.collectionLabel}, ${theme.label} variant` : `${theme.label} theme`}${isActive ? ", currently active" : ""}`}
305307 aria-pressed={isActive}
306 className="min-w-0 cursor-pointer truncate rounded-sm text-left text-sm font-medium text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card"
308 className="min-w-0 cursor-pointer truncate rounded-sm text-left text-sm font-medium text-foreground outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card"
307309 type="button"
308310 onClick={(event) => {
309311 event.stopPropagation();
......@@ -726,7 +728,7 @@ export function ThemeLibrary({
726728 aria-label={mode === "system" ? "Follow the system appearance" : `Use ${mode} mode`}
727729 aria-pressed={isActive}
728730 className={cn(
729 "flex cursor-pointer flex-col items-stretch gap-1.5 rounded-xl border p-2 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
731 "flex cursor-pointer flex-col items-stretch gap-1.5 rounded-xl border p-2 outline-none transition-colors focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring",
730732 isActive
731733 ? "border-transparent bg-accent/30"
732734 : "border-border/70 bg-card/60 hover:bg-accent/10",
......@@ -989,7 +991,7 @@ export function ThemeLibrary({
989991 <span className="absolute right-2 top-2 inline-grid size-5 grid-cols-1 sm:size-4">
990992 <input
991993 checked={checked}
992 className="col-start-1 row-start-1 size-full appearance-none rounded-sm border border-input bg-background outline-none checked:border-primary checked:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring dark:not-checked:bg-input/32 forced-colors:appearance-auto"
994 className="col-start-1 row-start-1 size-full appearance-none rounded-sm border border-input bg-background outline-none checked:border-primary checked:bg-primary focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring checked:focus-visible:inset-ring-3 checked:focus-visible:inset-ring-background dark:not-checked:bg-input/32 forced-colors:appearance-auto"
993995 id={checkboxId}
994996 name="themes-to-remove"
995997 type="checkbox"
apps/web/src/components/settings/settingsLayout.tsx+1-1
......@@ -375,7 +375,7 @@ export function SettingsRow({
375375 // Focusable so keyboard users can still reach the explanation.
376376 <span
377377 tabIndex={0}
378 className="flex w-full items-center rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring @min-[32rem]/settings-row:w-auto"
378 className="flex w-full items-center rounded-md outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring @min-[32rem]/settings-row:w-auto"
379379 />
380380 }
381381 >
apps/web/src/components/sidebar/SidebarChrome.tsx+1-1
......@@ -112,7 +112,7 @@ function SidebarBrand({ onBackdrop }: { onBackdrop: boolean }) {
112112 <Link
113113 aria-label="Go to threads"
114114 className={cn(
115 "relative z-10 ml-[var(--workspace-titlebar-content-left)] hidden h-7 w-fit min-w-0 shrink-0 items-center overflow-hidden rounded-md outline-hidden ring-ring focus-visible:ring-2 md:flex",
115 "relative z-10 ml-[var(--workspace-titlebar-content-left)] hidden h-7 w-fit min-w-0 shrink-0 items-center overflow-hidden rounded-md outline-hidden ring-ring focus-visible:ring-2 focus-visible:ring-inset md:flex",
116116 onBackdrop ? "text-white" : "text-foreground",
117117 )}
118118 to="/"
apps/web/src/components/sidebar/SidebarProviderUpdatePill.tsx+1-1
......@@ -195,7 +195,7 @@ export function SidebarProviderUpdatePill() {
195195 <button
196196 type="button"
197197 aria-label="Dismiss provider update notice"
198 className="relative z-[1] mr-1 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-md opacity-70 outline-none hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring"
198 className="relative z-[1] mr-1 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-md opacity-70 outline-none hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
199199 onClick={() => startExit(displayedView.key, null, displayedView.key)}
200200 >
201201 <XIcon className="size-3.5 shrink-0" />
apps/web/src/components/sidebar/SidebarUpdatePill.tsx+1-1
......@@ -303,7 +303,7 @@ function SidebarUpdateControl() {
303303 aria-label={tooltip}
304304 aria-disabled={isInteractionDisabled || undefined}
305305 className={cn(
306 "inline-flex size-8 items-center justify-center rounded-full outline-hidden ring-ring transition-colors focus-visible:ring-2",
306 "inline-flex size-8 items-center justify-center rounded-full outline-hidden ring-ring transition-colors focus-visible:ring-2 focus-visible:ring-inset",
307307 isInteractionDisabled ? "cursor-not-allowed" : "cursor-pointer",
308308 showUpdateIconState
309309 ? cn(
apps/web/src/components/sidebar/SidebarUpdateReleaseNotes.tsx+1-1
......@@ -30,7 +30,7 @@ function ReleaseLink({
3030}) {
3131 return (
3232 <a
33 className="mt-2 inline-flex items-center gap-1 rounded-sm text-xs leading-5 text-muted-foreground underline decoration-dotted underline-offset-4 outline-none transition-colors hover:text-foreground focus-visible:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
33 className="mt-2 inline-flex items-center gap-1 rounded-sm text-xs leading-5 text-muted-foreground underline decoration-dotted underline-offset-4 outline-none transition-colors hover:text-foreground focus-visible:text-foreground focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
3434 href={releaseUrl}
3535 onClick={(event) => {
3636 event.preventDefault();
apps/web/src/components/ui/badge.tsx+1-1
......@@ -7,7 +7,7 @@ import { cva, type VariantProps } from "class-variance-authority";
77import { cn } from "~/lib/utils";
88
99const badgeVariants = cva(
10 "relative inline-flex shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-sm border border-transparent font-medium outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-3.5 sm:[&_svg:not([class*='size-'])]:size-3 [&_svg]:pointer-events-none [&_svg]:shrink-0 [button&,a&]:cursor-pointer [button&,a&]:pointer-coarse:after:absolute [button&,a&]:pointer-coarse:after:size-full [button&,a&]:pointer-coarse:after:min-h-11 [button&,a&]:pointer-coarse:after:min-w-11",
10 "relative inline-flex shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-sm border border-transparent font-medium outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-3.5 sm:[&_svg:not([class*='size-'])]:size-3 [&_svg]:pointer-events-none [&_svg]:shrink-0 [button&,a&]:cursor-pointer [button&,a&]:pointer-coarse:after:absolute [button&,a&]:pointer-coarse:after:size-full [button&,a&]:pointer-coarse:after:min-h-11 [button&,a&]:pointer-coarse:after:min-w-11",
1111 {
1212 defaultVariants: {
1313 size: "default",
apps/web/src/components/ui/button.tsx+1-1
......@@ -8,7 +8,7 @@ import type * as React from "react";
88import { cn } from "~/lib/utils";
99
1010const buttonVariants = cva(
11 "[--control-icon-color:currentColor] [&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--control-radius)] border font-medium text-base outline-none transition-[box-shadow,scale] [&:active:not([aria-haspopup])]:scale-[0.97] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--control-radius)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 aria-disabled:cursor-not-allowed aria-disabled:opacity-64 sm:text-sm [&_svg:not([class*='text-'])]:text-[var(--control-icon-color)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
11 "[--control-icon-color:currentColor] [&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--control-radius)] border font-medium text-base outline-none transition-[box-shadow,scale] [&:active:not([aria-haspopup])]:scale-[0.97] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--control-radius)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 aria-disabled:cursor-not-allowed aria-disabled:opacity-64 sm:text-sm [&_svg:not([class*='text-'])]:text-[var(--control-icon-color)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1212 {
1313 defaultVariants: {
1414 size: "default",
apps/web/src/components/ui/calendar.tsx+2-2
......@@ -56,11 +56,11 @@ export function Calendar({
5656 day: "size-(--cell-size) text-sm py-px",
5757 day_button: cn(
5858 buttonClassNames,
59 "in-data-disabled:pointer-events-none in-[.range-middle]:rounded-none in-[.range-end:not(.range-start)]:rounded-s-none in-[.range-start:not(.range-end)]:rounded-e-none in-[.range-middle]:in-data-selected:bg-accent in-data-selected:bg-primary in-[.range-middle]:in-data-selected:text-foreground in-data-disabled:text-muted-foreground/72 in-data-outside:text-muted-foreground/72 in-data-selected:in-data-outside:text-primary-foreground in-data-selected:text-primary-foreground in-data-disabled:line-through outline-none in-[[data-selected]:not(.range-middle)]:transition-[border-radius,box-shadow] focus-visible:z-1 focus-visible:ring-[3px] focus-visible:ring-ring/50",
59 "in-data-disabled:pointer-events-none in-[.range-middle]:rounded-none in-[.range-end:not(.range-start)]:rounded-s-none in-[.range-start:not(.range-end)]:rounded-e-none in-[.range-middle]:in-data-selected:bg-accent in-data-selected:bg-primary in-[.range-middle]:in-data-selected:text-foreground in-data-disabled:text-muted-foreground/72 in-data-outside:text-muted-foreground/72 in-data-selected:in-data-outside:text-primary-foreground in-data-selected:text-primary-foreground in-data-disabled:line-through outline-none in-[[data-selected]:not(.range-middle)]:transition-[border-radius,box-shadow] focus-visible:z-1 focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-ring/50",
6060 ),
6161 dropdown: "absolute bg-popover inset-0 opacity-0",
6262 dropdown_root:
63 "relative has-focus:border-ring has-focus:ring-ring/50 has-focus:ring-[3px] border border-input shadow-xs/5 rounded-lg px-[calc(--spacing(3)-1px)] h-9 sm:h-8 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-me-1",
63 "relative has-focus:border-ring has-focus:ring-ring/50 has-focus:ring-[3px] has-focus:ring-inset border border-input shadow-xs/5 rounded-lg px-[calc(--spacing(3)-1px)] h-9 sm:h-8 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-me-1",
6464 dropdowns:
6565 "w-full flex items-center text-base sm:text-sm justify-center h-(--cell-size) gap-1.5 *:[span]:font-medium",
6666 hidden: "invisible",
apps/web/src/components/ui/checkbox.tsx+3-2
......@@ -8,14 +8,15 @@ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
88 return (
99 <CheckboxPrimitive.Root
1010 className={cn(
11 "relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-[.25rem] border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none ring-ring transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[3px] not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/48 data-disabled:opacity-64 sm:size-4 dark:not-data-checked:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
11 "relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-[.25rem] border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[3px] not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:outline-destructive/48 data-disabled:opacity-64 sm:size-4 dark:not-data-checked:bg-input/32 dark:focus-visible:aria-invalid:outline-destructive/24 dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
1212 className,
1313 )}
1414 data-slot="checkbox"
1515 {...props}
1616 >
17 {/* Leave a contrasting inner gap inside the 2px focus outline. */}
1718 <CheckboxPrimitive.Indicator
18 className="-inset-px absolute flex items-center justify-center rounded-[.25rem] text-primary-foreground data-unchecked:hidden data-checked:bg-primary data-indeterminate:text-foreground"
19 className="-inset-px absolute flex items-center justify-center rounded-[.25rem] text-primary-foreground data-unchecked:hidden data-checked:bg-primary data-indeterminate:text-foreground in-[[data-slot=checkbox]:focus-visible]:inset-shadow-[0_0_0_3px_var(--background)] in-[[data-slot=checkbox]:focus-visible]:outline-2 in-[[data-slot=checkbox]:focus-visible]:outline-solid in-[[data-slot=checkbox]:focus-visible]:-outline-offset-2 in-[[data-slot=checkbox]:focus-visible]:outline-inherit"
1920 data-slot="checkbox-indicator"
2021 render={(props, state) => (
2122 <span {...props}>
apps/web/src/components/ui/color-picker.tsx+2-2
......@@ -116,7 +116,7 @@ export function ColorSaturationValuePlane({
116116 aria-label={`${label} saturation and brightness`}
117117 role="group"
118118 className={cn(
119 "relative cursor-crosshair touch-none overflow-hidden bg-[linear-gradient(to_top,#000,transparent),linear-gradient(to_right,#fff,transparent)] has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 has-[:focus-visible]:ring-offset-popover",
119 "relative cursor-crosshair touch-none overflow-hidden bg-[linear-gradient(to_top,#000,transparent),linear-gradient(to_right,#fff,transparent)] has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-inset has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 has-[:focus-visible]:ring-offset-popover",
120120 variant === "edge" ? "h-36 rounded-none" : "h-32 rounded-lg",
121121 className,
122122 )}
......@@ -200,7 +200,7 @@ export function ColorHueSlider({
200200 aria-valuemin={0}
201201 aria-valuenow={Math.round(value)}
202202 className={cn(
203 "relative flex h-6 cursor-pointer touch-none items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover",
203 "relative flex h-6 cursor-pointer touch-none items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover",
204204 className,
205205 )}
206206 role="slider"
apps/web/src/components/ui/input-group.tsx+1-1
......@@ -7,7 +7,7 @@ import { cn } from "~/lib/utils";
77import { Input, type InputProps } from "~/components/ui/input";
88
99const inputGroupVariants = cva(
10 "relative inline-flex w-full min-w-0 items-center rounded-[var(--control-radius)] border text-base text-foreground ring-ring/24 transition-shadow has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/64 has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/16 has-[textarea]:h-auto has-data-[align=block-end]:h-auto has-data-[align=block-start]:h-auto has-data-[align=block-end]:flex-col has-data-[align=block-start]:flex-col has-[input:focus-visible,textarea:focus-visible]:border-ring has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/36 has-autofill:bg-foreground/4 has-[input:disabled,textarea:disabled]:opacity-64 has-[input:disabled,textarea:disabled,input:focus-visible,textarea:focus-visible,input[aria-invalid],textarea[aria-invalid]]:shadow-none has-[input:focus-visible,textarea:focus-visible]:ring-[3px] sm:text-sm dark:has-autofill:bg-foreground/8 dark:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/24 has-data-[align=inline-start]:**:[[data-size=sm]_input]:ps-1.5 has-data-[align=inline-end]:**:[[data-size=sm]_input]:pe-1.5 *:[[data-slot=input-control],[data-slot=textarea-control]]:contents *:[[data-slot=input-control],[data-slot=textarea-control]]:before:hidden has-[[data-align=block-start],[data-align=block-end]]:**:[input]:h-auto has-data-[align=inline-start]:**:[input]:ps-2 has-data-[align=inline-end]:**:[input]:pe-2 has-data-[align=block-end]:**:[input]:pt-1.5 has-data-[align=block-start]:**:[input]:pb-1.5 **:[textarea]:min-h-20.5 **:[textarea]:resize-none **:[textarea]:py-[calc(--spacing(3)-1px)] **:[textarea]:max-sm:min-h-23.5 **:[textarea_button]:rounded-[calc(var(--control-radius)-1px)]",
10 "relative inline-flex w-full min-w-0 items-center rounded-[var(--control-radius)] border text-base text-foreground ring-ring/24 transition-shadow has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/64 has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/16 has-[textarea]:h-auto has-data-[align=block-end]:h-auto has-data-[align=block-start]:h-auto has-data-[align=block-end]:flex-col has-data-[align=block-start]:flex-col has-[input:focus-visible,textarea:focus-visible]:border-ring has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/36 has-autofill:bg-foreground/4 has-[input:disabled,textarea:disabled]:opacity-64 has-[input:disabled,textarea:disabled,input:focus-visible,textarea:focus-visible,input[aria-invalid],textarea[aria-invalid]]:shadow-none has-[input:focus-visible,textarea:focus-visible]:ring-[3px] has-[input:focus-visible,textarea:focus-visible]:ring-inset sm:text-sm dark:has-autofill:bg-foreground/8 dark:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/24 has-data-[align=inline-start]:**:[[data-size=sm]_input]:ps-1.5 has-data-[align=inline-end]:**:[[data-size=sm]_input]:pe-1.5 *:[[data-slot=input-control],[data-slot=textarea-control]]:contents *:[[data-slot=input-control],[data-slot=textarea-control]]:before:hidden has-[[data-align=block-start],[data-align=block-end]]:**:[input]:h-auto has-data-[align=inline-start]:**:[input]:ps-2 has-data-[align=inline-end]:**:[input]:pe-2 has-data-[align=block-end]:**:[input]:pt-1.5 has-data-[align=block-start]:**:[input]:pb-1.5 **:[textarea]:min-h-20.5 **:[textarea]:resize-none **:[textarea]:py-[calc(--spacing(3)-1px)] **:[textarea]:max-sm:min-h-23.5 **:[textarea_button]:rounded-[calc(var(--control-radius)-1px)]",
1111 {
1212 defaultVariants: {
1313 variant: "default",
apps/web/src/components/ui/input.tsx+1-1
......@@ -65,7 +65,7 @@ function Input({
6565 className={
6666 cn(
6767 !unstyled &&
68 "relative inline-flex w-full rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-autofill:bg-foreground/4 has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] sm:text-sm dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
68 "relative inline-flex w-full rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-autofill:bg-foreground/4 has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] has-focus-visible:ring-inset sm:text-sm dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
6969 !unstyled &&
7070 size === "compact" &&
7171 "rounded-md before:rounded-[calc(var(--radius-md)-1px)]",
apps/web/src/components/ui/menu.tsx+1-1
......@@ -145,7 +145,7 @@ function MenuCheckboxItem({
145145 <>
146146 <span className="col-start-1">{children}</span>
147147 <MenuPrimitive.CheckboxItemIndicator
148 className="inset-shadow-[0_1px_--theme(--color-black/4%)] inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]"
148 className="inset-shadow-[0_1px_--theme(--color-black/4%)] inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]"
149149 keepMounted
150150 >
151151 <span className="pointer-events-none block aspect-square h-full in-[[data-slot=menu-checkbox-item][data-checked]]:origin-[var(--thumb-size)_50%] origin-left in-[[data-slot=menu-checkbox-item][data-checked]]:translate-x-[calc(var(--thumb-size)-4px)] in-[[data-slot=menu-checkbox-item]:active]:not-data-disabled:scale-x-110 in-[[data-slot=menu-checkbox-item]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.10)] rounded-(--thumb-size) bg-background shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s]" />
apps/web/src/components/ui/number-field.tsx+1-1
......@@ -33,7 +33,7 @@ export function NumberFieldGroup({
3333 return (
3434 <NumberFieldPrimitive.Group
3535 className={cn(
36 "relative flex w-full justify-between rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-within:border-ring focus-within:ring-[3px] has-aria-invalid:border-destructive/36 has-autofill:bg-foreground/4 focus-within:has-aria-invalid:border-destructive/64 focus-within:has-aria-invalid:ring-destructive/48 data-disabled:pointer-events-none data-disabled:opacity-64 sm:text-sm dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 [[data-disabled],:focus-within,[aria-invalid]]:shadow-none",
36 "relative flex w-full justify-between rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-inset has-aria-invalid:border-destructive/36 has-autofill:bg-foreground/4 focus-within:has-aria-invalid:border-destructive/64 focus-within:has-aria-invalid:ring-destructive/48 data-disabled:pointer-events-none data-disabled:opacity-64 sm:text-sm dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 [[data-disabled],:focus-within,[aria-invalid]]:shadow-none",
3737 className,
3838 )}
3939 data-slot="number-field-group"
apps/web/src/components/ui/radio-group.tsx+3-2
......@@ -19,14 +19,15 @@ function Radio({ className, ...props }: RadioPrimitive.Root.Props) {
1919 return (
2020 <RadioPrimitive.Root
2121 className={cn(
22 "relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-full border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-full not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/48 data-disabled:opacity-64 sm:size-4 dark:not-data-checked:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
22 "relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-full border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-full not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:outline-destructive/48 data-disabled:opacity-64 sm:size-4 dark:not-data-checked:bg-input/32 dark:focus-visible:aria-invalid:outline-destructive/24 dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
2323 className,
2424 )}
2525 data-slot="radio"
2626 {...props}
2727 >
28 {/* Leave a contrasting inner gap inside the 2px focus outline. */}
2829 <RadioPrimitive.Indicator
29 className="-inset-px absolute flex size-4.5 items-center justify-center rounded-full before:size-2 before:rounded-full before:bg-primary-foreground data-unchecked:hidden data-checked:bg-primary sm:size-4 sm:before:size-1.5"
30 className="-inset-px absolute flex size-4.5 items-center justify-center rounded-full before:size-2 before:rounded-full before:bg-primary-foreground data-unchecked:hidden data-checked:bg-primary sm:size-4 sm:before:size-1.5 in-[[data-slot=radio]:focus-visible]:inset-shadow-[0_0_0_3px_var(--background)] in-[[data-slot=radio]:focus-visible]:outline-2 in-[[data-slot=radio]:focus-visible]:outline-solid in-[[data-slot=radio]:focus-visible]:-outline-offset-2 in-[[data-slot=radio]:focus-visible]:outline-inherit"
3031 data-slot="radio-indicator"
3132 />
3233 </RadioPrimitive.Root>
apps/web/src/components/ui/scroll-area.tsx+3-2
......@@ -57,11 +57,12 @@ function ScrollArea({
5757 <ScrollAreaPrimitive.Viewport
5858 {...(viewportTabIndex === undefined ? {} : { tabIndex: viewportTabIndex })}
5959 className={cn(
60 "h-full max-h-[inherit] overflow-auto overscroll-contain rounded-[inherit] outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-x:overscroll-x-contain",
60 "h-full max-h-[inherit] overflow-auto overscroll-contain rounded-[inherit] outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-x:overscroll-x-contain",
6161 chainVerticalScroll && "overscroll-y-auto",
6262 scrollFade &&
63 "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]",
63 "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] focus-visible:mask-none [--fade-size:1.5rem]",
6464 scrollFade && scrollFadePadding && "scroll-p-[var(--fade-size)]",
65 scrollFade && !scrollFadePadding && "has-focus-visible:mask-none",
6566 scrollbarGutter && "scrollbar-gutter-stable",
6667 hideScrollbars &&
6768 "[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
apps/web/src/components/ui/select.tsx+2-2
......@@ -20,9 +20,9 @@ const selectTriggerVariants = cva(
2020 variants: {
2121 variant: {
2222 default:
23 "w-full min-w-36 border-input bg-background not-dark:bg-clip-padding text-foreground shadow-xs/5 ring-ring/24 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 focus-visible:border-ring focus-visible:ring-[3px] aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='text-'])]:text-icon-muted [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none",
23 "w-full min-w-36 border-input bg-background not-dark:bg-clip-padding text-foreground shadow-xs/5 ring-ring/24 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-inset aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='text-'])]:text-icon-muted [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none",
2424 ghost:
25 "border-transparent text-secondary-label focus-visible:ring-2 focus-visible:ring-ring data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent [:hover,[data-pressed]]:text-foreground",
25 "border-transparent text-secondary-label focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent [:hover,[data-pressed]]:text-foreground",
2626 },
2727 size: {
2828 compact:
apps/web/src/components/ui/sidebar.tsx+2-2
......@@ -660,7 +660,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
660660}
661661
662662const sidebarMenuButtonVariants = cva(
663 "peer/menu-button flex w-full cursor-pointer items-center gap-[var(--sidebar-control-gap)] overflow-hidden text-left outline-hidden ring-ring transition-[width,height,padding] hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-row-active active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-64 aria-disabled:pointer-events-none aria-disabled:opacity-64 data-[active=true]:bg-sidebar-row-selected data-[active=true]:font-medium data-[active=true]:text-sidebar-foreground data-[state=open]:hover:bg-sidebar-row-hover data-[state=open]:hover:text-sidebar-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-[var(--sidebar-content-inset)]! [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-[var(--sidebar-icon-color)] hover:[&>svg]:text-sidebar-foreground active:[&>svg]:text-sidebar-foreground data-[active=true]:[&>svg]:text-sidebar-foreground",
663 "peer/menu-button flex w-full cursor-pointer items-center gap-[var(--sidebar-control-gap)] overflow-hidden text-left outline-hidden ring-ring transition-[width,height,padding] hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-inset active:bg-sidebar-row-active active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-64 aria-disabled:pointer-events-none aria-disabled:opacity-64 data-[active=true]:bg-sidebar-row-selected data-[active=true]:font-medium data-[active=true]:text-sidebar-foreground data-[state=open]:hover:bg-sidebar-row-hover data-[state=open]:hover:text-sidebar-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-[var(--sidebar-content-inset)]! [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-[var(--sidebar-icon-color)] hover:[&>svg]:text-sidebar-foreground active:[&>svg]:text-sidebar-foreground data-[active=true]:[&>svg]:text-sidebar-foreground",
664664 {
665665 defaultVariants: {
666666 size: "default",
......@@ -773,7 +773,7 @@ function SidebarMenuSubButton({
773773}) {
774774 const defaultProps = {
775775 className: cn(
776 "-translate-x-px flex h-7 min-w-0 cursor-pointer items-center gap-2 overflow-hidden rounded-lg px-2 text-sidebar-foreground outline-hidden ring-ring hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-row-active active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-64 aria-disabled:pointer-events-none aria-disabled:opacity-64 [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-muted-foreground",
776 "-translate-x-px flex h-7 min-w-0 cursor-pointer items-center gap-2 overflow-hidden rounded-lg px-2 text-sidebar-foreground outline-hidden ring-ring hover:bg-sidebar-row-hover hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-inset active:bg-sidebar-row-active active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-64 aria-disabled:pointer-events-none aria-disabled:opacity-64 [&>span:last-child]:truncate [&>svg:not([class*='size-'])]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-muted-foreground",
777777 "data-[active=true]:bg-sidebar-row-selected data-[active=true]:text-sidebar-foreground",
778778 size === "sm" && "text-xs",
779779 size === "md" && "text-sm",
apps/web/src/components/ui/switch.tsx+1-1
......@@ -18,7 +18,7 @@ function Switch({
1818 return (
1919 <SwitchPrimitive.Root
2020 className={cn(
21 "inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 cursor-pointer items-center rounded-full p-[2px] outline-none transition-[background-color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:opacity-64 data-[mixed]:bg-input",
21 "inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 cursor-pointer items-center rounded-full p-[2px] outline-none transition-[background-color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:opacity-64 data-[mixed]:bg-input",
2222 size === "sm"
2323 ? "[--thumb-size:--spacing(4)] sm:[--thumb-size:--spacing(3.5)]"
2424 : "[--thumb-size:--spacing(5)] sm:[--thumb-size:--spacing(4)]",
apps/web/src/components/ui/textarea.tsx+1-1
......@@ -17,7 +17,7 @@ function Textarea({ className, size = "default", unstyled = false, ...props }: T
1717 className={
1818 cn(
1919 !unstyled &&
20 "relative inline-flex w-full rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] sm:text-sm dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
20 "relative inline-flex w-full rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-aria-invalid:border-destructive/36 has-focus-visible:border-ring has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none has-focus-visible:ring-[3px] has-focus-visible:ring-inset not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] sm:text-sm dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
2121 className,
2222 ) || undefined
2323 }
apps/web/src/components/ui/toast.tsx+2-2
......@@ -98,7 +98,7 @@ const toastCornerDismissClass = "absolute z-20 -top-1.5 -right-1.5";
9898const toastCornerOrbClass = cn(
9999 "inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border border-border/60 bg-popover/92 text-muted-foreground shadow-sm outline-none backdrop-blur-sm",
100100 "transition-[color,background-color,box-shadow] hover:bg-popover hover:text-foreground",
101 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
101 "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
102102);
103103
104104function handleToastDismissClick(
......@@ -225,7 +225,7 @@ function ToastDescriptionAndExpandable({
225225 className={cn(
226226 "group flex min-w-0 w-full cursor-pointer select-none items-start gap-1.5 rounded-sm text-left outline-none ring-offset-background",
227227 "transition-colors hover:bg-muted/40",
228 "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
228 "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
229229 )}
230230 onClick={toggle}
231231 onKeyDown={onKeyDown}
apps/web/src/components/ui/toggle.tsx+1-1
......@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
66import { cn } from "~/lib/utils";
77
88const toggleVariants = cva(
9 "[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base text-foreground outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 aria-disabled:opacity-64 data-pressed:bg-input/64 data-pressed:text-accent-foreground sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
9 "[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base text-foreground outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 aria-disabled:opacity-64 data-pressed:bg-input/64 data-pressed:text-accent-foreground sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1010 {
1111 defaultVariants: {
1212 size: "default",
apps/web/src/components/ui/wizard.tsx+2-2
......@@ -96,11 +96,11 @@ export function WizardSteps({
9696 ? { type: "button" as const, disabled: isStepDisabled?.(index) }
9797 : {})}
9898 className={cn(
99 "flex w-full min-w-0 items-center gap-2 rounded-lg px-2.5 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring max-sm:justify-center max-sm:px-2",
99 "flex w-full min-w-0 items-center gap-2 rounded-lg px-2.5 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring max-sm:justify-center max-sm:px-2",
100100 onStepChange &&
101101 "cursor-pointer hover:bg-card disabled:cursor-default disabled:hover:bg-transparent",
102102 index === currentStep &&
103 "bg-card text-foreground shadow-xs ring-1 ring-black/5 hover:bg-card dark:shadow-none dark:ring-white/5",
103 "bg-card text-foreground shadow-xs ring-1 ring-inset ring-black/5 hover:bg-card dark:shadow-none dark:ring-white/5",
104104 )}
105105 aria-current={index === currentStep ? "step" : undefined}
106106 aria-label={`${step}, step ${index + 1}${index < currentStep && summaries?.[index] ? `, ${summaries?.[index]}` : ""}`}
apps/web/src/components/usage/UsageLimits.tsx+1-1
......@@ -110,7 +110,7 @@ function WindowBar({
110110 role="img"
111111 aria-label={summary}
112112 tabIndex={0}
113 className="relative h-6 cursor-default rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
113 className="relative h-6 cursor-default rounded-full outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
114114 />
115115 }
116116 >
apps/web/src/components/usage/UsageLimitsPooled.tsx+1-1
......@@ -255,7 +255,7 @@ function PoolSegment({
255255 type="button"
256256 style={{ gridColumn: index, gridRow: 1 }}
257257 aria-label={`${account.displayName ?? (account.email ? accountInitials(account.email) : account.driver)}: ${remaining}% left${resetsIn ? `, ${resetsIn}` : ""}${credits ? `, ${credits} reset ${credits === 1 ? "credit" : "credits"} banked` : ""}`}
258 className="relative h-5 min-w-0 cursor-pointer overflow-hidden rounded-md bg-muted text-start outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[popup-open]:ring-1 data-[popup-open]:ring-border @2xl/pool:h-8"
258 className="relative h-5 min-w-0 cursor-pointer overflow-hidden rounded-md bg-muted text-start outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[popup-open]:ring-1 data-[popup-open]:ring-inset data-[popup-open]:ring-border @2xl/pool:h-8"
259259 />
260260 }
261261 >
apps/web/src/index.css+13-7
......@@ -684,6 +684,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
684684 * {
685685 @apply border-border outline-ring/50;
686686 }
687 /* The app's 2px outlines stay inside controls at clipped and scrollport edges. */
688 *,
689 ::before,
690 ::after {
691 outline-offset: -2px;
692 }
687693 :where([data-slot="menu-popup"], [data-slot="select-popup"], [data-slot="popover-popup"]):focus,
688694 :where(
689695 [data-slot="menu-popup"],
......@@ -887,10 +893,10 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
887893 @keyframes settings-search-target-pulse {
888894 0%,
889895 100% {
890 box-shadow: 0 0 0 0 transparent;
896 box-shadow: inset 0 0 0 0 transparent;
891897 }
892898 50% {
893 box-shadow: 0 0 0 2px color-mix(in oklab, var(--color-primary) 70%, transparent);
899 box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--color-primary) 70%, transparent);
894900 }
895901 }
896902
......@@ -1015,14 +1021,14 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
10151021
10161022 .settings-slider:focus-visible::-webkit-slider-thumb {
10171023 box-shadow:
1018 0 0 0 3px var(--background),
1019 0 0 0 5px var(--ring);
1024 inset 0 0 0 1px var(--background),
1025 inset 0 0 0 3px var(--ring);
10201026 }
10211027
10221028 .settings-slider:focus-visible::-moz-range-thumb {
10231029 box-shadow:
1024 0 0 0 3px var(--background),
1025 0 0 0 5px var(--ring);
1030 inset 0 0 0 1px var(--background),
1031 inset 0 0 0 3px var(--ring);
10261032 }
10271033
10281034 @media (forced-colors: active) {
......@@ -2255,7 +2261,7 @@ code {
22552261/* Selected chips (node selection from arrowing into or clicking a chip). */
22562262.composer-tiptap .ProseMirror-selectednode {
22572263 outline: 2px solid var(--ring);
2258 outline-offset: 1px;
2264 outline-offset: -2px;
22592265 border-radius: 6px;
22602266}
22612267
oxlint-plugin-t3code/index.ts+2
......@@ -7,6 +7,7 @@ import noInlineSchemaCompile from "./rules/no-inline-schema-compile.ts";
77import noManualEffectRuntimeInTests from "./rules/no-manual-effect-runtime-in-tests.ts";
88import noMobileUniwindThemeEscapeHatches from "./rules/no-mobile-uniwind-theme-escape-hatches.ts";
99import noNativeTitleTooltip from "./rules/no-native-title-tooltip.ts";
10import noOutsetStateIndicators from "./rules/no-outset-state-indicators.ts";
1011import noRawMcpRegistration from "./rules/no-raw-mcp-registration.ts";
1112import noTestInLoop from "./rules/no-test-in-loop.ts";
1213import noRpcPermissionBypass from "./rules/no-rpc-permission-bypass.ts";
......@@ -27,6 +28,7 @@ export default definePlugin({
2728 "no-manual-effect-runtime-in-tests": noManualEffectRuntimeInTests,
2829 "no-mobile-uniwind-theme-escape-hatches": noMobileUniwindThemeEscapeHatches,
2930 "no-native-title-tooltip": noNativeTitleTooltip,
31 "no-outset-state-indicators": noOutsetStateIndicators,
3032 "no-raw-mcp-registration": noRawMcpRegistration,
3133 "no-test-in-loop": noTestInLoop,
3234 "no-rpc-permission-bypass": noRpcPermissionBypass,
oxlint-plugin-t3code/rules/no-outset-state-indicators.ts created+126
......@@ -0,0 +1,126 @@
1import { defineRule, type ESTree } from "@oxlint/plugins";
2
3const CLASS_COMPOSERS = new Set(["cn", "clsx", "classNames", "cva", "twMerge"]);
4const STATE_VARIANT =
5 /(?:^|[^\w])(?:focus(?:-visible|-within)?|selected|checked|pressed)(?=$|[^\w])/u;
6const RING_WIDTH = /^ring(?:-(?:[1-9]\d*|\[[^\]]+\]|\(length:[^)]+\)))?$/u;
7// Pixel widths only; arbitrary values like `outline-[#243c5a]` are colors.
8const OUTLINE_WIDTH = /^outline-(?:(\d+)|\[(?:length:)?(\d+(?:\.\d+)?)px\])$/u;
9const INWARD_OFFSET = /^-outline-offset-(\d+)$/u;
10
11/** Splits `dark:[&_a]:ring-2!` into its variants and utility, ignoring colons inside brackets. */
12function parseClass(token: string) {
13 const variants: string[] = [];
14 let depth = 0;
15 let start = 0;
16 for (let index = 0; index < token.length; index++) {
17 const character = token[index];
18 if (character === "[" || character === "(") depth++;
19 else if (character === "]" || character === ")") depth--;
20 else if (character === ":" && depth === 0) {
21 variants.push(token.slice(start, index));
22 start = index + 1;
23 }
24 }
25 return { variants, utility: token.slice(start).replace(/^!|!$/gu, "") };
26}
27
28type ParsedClass = ReturnType<typeof parseClass>;
29
30/** A companion applies when its variants are a subset of the indicator's variants. */
31const covers = (companion: ParsedClass, indicator: ParsedClass) =>
32 companion.variants.every((variant) => indicator.variants.includes(variant));
33
34const isClassComposer = (node: ESTree.Node) =>
35 node.type === "CallExpression" &&
36 node.callee.type === "Identifier" &&
37 CLASS_COMPOSERS.has(node.callee.name);
38
39/** Strings a composition always includes; branches, class maps, and other calls are not guaranteed. */
40function collectStrings(node: unknown, out: string[]) {
41 if (Array.isArray(node)) {
42 for (const child of node) collectStrings(child, out);
43 return;
44 }
45 if (typeof node !== "object" || node === null || !("type" in node)) return;
46 const current = node as ESTree.Node;
47 if (/^(?:Conditional|Logical|Object)Expression$/u.test(current.type)) return;
48 if (current.type === "CallExpression" && !isClassComposer(current)) return;
49 if (current.type === "Literal" && typeof current.value === "string") out.push(current.value);
50 if (current.type === "TemplateElement") out.push(current.value.cooked ?? current.value.raw);
51 for (const [key, value] of Object.entries(current)) {
52 if (key !== "parent") collectStrings(value, out);
53 }
54}
55
56/** Classes guaranteed alongside a node by each enclosing `cn(...)`, so sibling companions count. */
57function scopeClasses(node: ESTree.Node, own: ParsedClass[]) {
58 const strings: string[] = [];
59 for (let current = node.parent; current; current = current.parent) {
60 if (/Statement$|Declaration$|^JSX(?:Attribute|Element)$/u.test(current.type)) break;
61 if (isClassComposer(current)) collectStrings(current, strings);
62 }
63 return [...own, ...strings.join(" ").split(/\s+/u).map(parseClass)];
64}
65
66function offenders(text: string, node: ESTree.Node) {
67 const own = text.split(/\s+/u).filter(Boolean).map(parseClass);
68 const found: string[] = [];
69 let scope: ParsedClass[] | undefined;
70 for (const token of own) {
71 const { utility, variants } = token;
72 const label = [...variants, utility].join(":");
73 // Positive offsets push the outline out of the element; the app default is inward.
74 if (/^outline-offset-|^\[outline-offset:/u.test(utility)) {
75 found.push(label);
76 continue;
77 }
78 if (!STATE_VARIANT.test(variants.join(":"))) continue;
79 if (RING_WIDTH.test(utility)) {
80 scope ??= scopeClasses(node, own);
81 const inset = scope.some((other) => other.utility === "ring-inset" && covers(other, token));
82 if (!inset) found.push(label);
83 continue;
84 }
85 const outline = OUTLINE_WIDTH.exec(utility);
86 if (outline) {
87 const width = Number(outline[1] ?? outline[2]);
88 if (width <= 2) continue;
89 scope ??= scopeClasses(node, own);
90 const inward = scope.some((other) => {
91 const offset = INWARD_OFFSET.exec(other.utility);
92 return offset !== null && Number(offset[1]) >= width && covers(other, token);
93 });
94 if (!inward) found.push(label);
95 }
96 }
97 return found;
98}
99
100export default defineRule({
101 meta: {
102 type: "problem",
103 docs: {
104 description:
105 "Keep focus and selection indicators inside their element, where an ancestor cannot clip them.",
106 },
107 },
108 create(context) {
109 const check = (node: ESTree.Node, text: string) => {
110 for (const utility of offenders(text, node)) {
111 context.report({
112 node,
113 message: `${utility} paints a focus or selection indicator outside its element, where an ancestor can clip it. Add ring-inset under the same variant, drop the positive outline offset, or use a negative offset at least as wide as the outline.`,
114 });
115 }
116 };
117 return {
118 Literal(node) {
119 if (typeof node.value === "string") check(node, node.value);
120 },
121 TemplateLiteral(node) {
122 check(node, node.quasis.map((part) => part.value.cooked ?? part.value.raw).join(" "));
123 },
124 };
125 },
126});
oxlint-plugin-t3code/rules/no-unscoped-has.test.ts+39
......@@ -122,3 +122,42 @@ describe("t3code/no-unscoped-has", () => {
122122 `const className = "[body:has([data-dialog-open])_&]:overflow-hidden";`,
123123 );
124124});
125
126const indicators = createOxlintRuleHarness("t3code/no-outset-state-indicators", {
127 filename: "fixture.tsx",
128});
129
130describe("t3code/no-outset-state-indicators", () => {
131 describe.each(
132 Object.entries({
133 "inset state rings": `const el = <button className="focus-visible:ring-2 focus-visible:ring-inset ring-1 ring-border" />;`,
134 "an unconditional inset": `const el = <button className="ring-inset data-selected:ring-2" />;`,
135 "an inset in a sibling composer argument": `const value = cn(active && "dark:focus-visible:ring-2", "focus-visible:ring-inset");`,
136 "an inset in a nested conditional composer": `const value = cn(enabled && cn("focus-visible:ring-2", "focus-visible:ring-inset"));`,
137 "outlines covered by the inward default": `const el = <button className="focus-visible:outline-2 focus-visible:outline-ring" />;`,
138 "wider outlines with a matching inward offset": `const el = <button className="focus-visible:outline-3 focus-visible:-outline-offset-3" />;`,
139 "arbitrary pixel outlines with a matching inward offset": `const el = <button className="focus-visible:outline-[3px] focus-visible:-outline-offset-3" />;`,
140 "arbitrary outline colors": `const el = <button className="focus-visible:outline-[#243c5a]" />;`,
141 "decorative rings without a state": `const el = <img className="ring-2 ring-background" />;`,
142 prose: `const note = "the focus ring stays visible";`,
143 }),
144 )("valid: %s", (name, source) => {
145 indicators.valid(name, source);
146 });
147
148 describe.each(
149 Object.entries({
150 "state rings without an inset": `const el = <button className="focus-visible:ring-2 focus-visible:ring-ring" />;`,
151 "an inset from another state": `const el = <button className="focus:ring-inset focus-visible:ring-2" />;`,
152 "an inset from another conditional branch": `const value = cn(active ? "focus:ring-2" : "ring-inset");`,
153 "an inset passed to another function": `const value = cn("focus-visible:ring-2", label("ring-inset"));`,
154 "arbitrary state ring widths": `const value = cn("has-[:focus-visible]:ring-[3px]");`,
155 "positive outline offsets": `const el = <button className="focus-visible:outline-2 focus-visible:outline-offset-2" />;`,
156 "arbitrary outline offsets": `const el = <button className="focus-visible:[outline-offset:2px]" />;`,
157 "wide outlines without an inward offset":
158 "const className = `focus-visible:outline-4 ${layout}`;",
159 }),
160 )("invalid: %s", (name, source) => {
161 indicators.invalid(name, source);
162 });
163});
packages/shared/src/htmlRender.ts+3-1
......@@ -325,7 +325,9 @@ export function htmlRenderThemeMessage(theme: HtmlRenderTheme) {
325325// reply reads as a box within the thread, so it stays hidden.
326326const BASE_CSS =
327327 "html{background:var(--background);color:var(--foreground);font-family:var(--font-sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;scrollbar-width:none}" +
328 "html::-webkit-scrollbar{display:none}body{margin:0}code,kbd,pre,samp{font-family:var(--font-mono)}";
328 "html::-webkit-scrollbar{display:none}body{margin:0}code,kbd,pre,samp{font-family:var(--font-mono)}" +
329 // Focus outlines paint inside the element, so the frame edge never clips them.
330 ":where(:focus-visible){outline:2px solid var(--ring)}:focus-visible{outline-width:2px!important;outline-offset:-2px!important}";
329331
330332function rootRule(theme: HtmlRenderTheme): string {
331333 const declarations = Object.entries(theme.variables)
vite.config.ts+5
......@@ -256,6 +256,11 @@ export default defineConfig({
256256 files: ["apps/web/src/**"],
257257 rules: { "shadcn/no-unknown-classes": "error" },
258258 },
259 {
260 // State indicators paint inward; decorative rings keep their own geometry.
261 files: ["apps/web/src/**"],
262 rules: { "t3code/no-outset-state-indicators": "error" },
263 },
259264 {
260265 // Colors come from theme tokens so status tones follow custom themes. components/ui
261266 // has no findings and stays covered too.