Skip to content

Commit 16cf357

Browse files
authored
Focus visible style refinements (#1577)
* reduces the thickness of the focus-visible * More subtle Input style * Updates focus-visible styles for the SimpleSelect * Tables tab through rows and action cells only * Refined focus visible style * Adds the custom focus visible to the TextLink * Moves the onClick handling to the table row rather than the cell * Makes table row heights consistent * Adds gap between task search bar and button * Prevents long tag values from wrapping * Removes unnecessary rows from table header * Added gap between search and filters * Fixes the schedules table pagination staying fixed to the bottom of the page * Removed more unnecessary header table rows to prevent them being selectable * Removed isSelected styles (not working) * Added <tr> back to the main Table compontent * Table row handles modifier keys * Adds to={path} to the TableRow only * Revert "Adds to={path} to the TableRow only" This reverts commit 8a814d4. * Revert "Table row handles modifier keys" This reverts commit b239474. * Table reverted to use linked cells rather than rows * Set the tab index of a cell and style the table row when tabbed * Tabbed row style applied to the sticky cells * Adds isTabbableCell to each table * Improves the spcificity of the row highlighting * Reduces the height of the task rows to match the other tables * Adds tab styles to fill in row dividers top and bottom * Removed old row onClick and to props * Removed duplicate table header row * Creates table style variants so tables look good in the inspector panels * Full width tables on the schedules inspector panel * Reduce padding on Alerts page table rows * Updates the Deploy page inspector table to the new style * Removes the duplicate Table Row from the table headers in v2 * Adds TableRow to the EventsTable * Adds TableRow to more tables * Adds TableRow to more tables * Adds TableRow to more tables * Adds TableRow to more tables * Fix for showing correct cursor for linked table rows * Removed variants from some of the table components
1 parent f96bf72 commit 16cf357

File tree

16 files changed

+298
-229
lines changed

16 files changed

+298
-229
lines changed

apps/webapp/app/components/primitives/Input.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { cn } from "~/utils/cn";
44
import { Icon, RenderIcon } from "./Icon";
55

66
const containerBase =
7-
"has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-text-link has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-2 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text";
7+
"has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-charcoal-650 has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-1 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text";
88

99
const inputBase =
1010
"h-full w-full text-text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none";
@@ -17,27 +17,27 @@ const variants = {
1717
container:
1818
"px-1 w-full h-10 rounded-[3px] border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
1919
input: "px-2 text-sm",
20-
iconSize: "h-4 w-4 ml-1",
20+
iconSize: "size-4 ml-1",
2121
shortcut: "mr-1 min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem] select-none",
2222
},
2323
medium: {
2424
container:
2525
"px-1 h-8 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
2626
input: "px-1.5 rounded text-sm",
27-
iconSize: "h-4 w-4 ml-0.5",
27+
iconSize: "size-4 ml-0.5",
2828
shortcut: "min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem]",
2929
},
3030
small: {
3131
container:
3232
"px-1 h-6 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
3333
input: "px-1 rounded text-xs",
34-
iconSize: "h-3 w-3 ml-0.5",
34+
iconSize: "size-3 ml-0.5",
3535
shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]",
3636
},
3737
tertiary: {
3838
container: "px-1 h-6 w-full rounded hover:bg-charcoal-750",
3939
input: "px-1 rounded text-xs",
40-
iconSize: "h-3 w-3 ml-0.5",
40+
iconSize: "size-3 ml-0.5",
4141
shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]",
4242
},
4343
};

apps/webapp/app/components/primitives/SimpleSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const SelectTrigger = React.forwardRef<
2929
<SelectPrimitive.Trigger
3030
ref={ref}
3131
className={cn(
32-
"ring-offset-background focus-visible:ring-ring group flex items-center justify-between gap-x-1 rounded text-text-dimmed transition placeholder:text-text-dimmed hover:text-text-bright focus-visible:bg-tertiary focus-visible:text-text-bright focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
32+
"ring-offset-background group flex items-center justify-between gap-x-1 rounded text-text-dimmed transition placeholder:text-text-dimmed hover:text-text-bright focus-visible:focus-custom disabled:cursor-not-allowed disabled:opacity-50",
3333
width === "full" ? "w-full" : "w-min",
3434
sizeClassName,
3535
className

apps/webapp/app/components/primitives/Table.tsx

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,59 @@
11
import { ChevronRightIcon } from "@heroicons/react/24/solid";
22
import { Link } from "@remix-run/react";
3-
import { ReactNode, forwardRef, useState } from "react";
3+
import React, { ReactNode, forwardRef, useState, useContext, createContext } from "react";
44
import { cn } from "~/utils/cn";
55
import { Popover, PopoverContent, PopoverVerticalEllipseTrigger } from "./Popover";
66
import { InfoIconTooltip } from "./Tooltip";
77

8+
const variants = {
9+
bright: {
10+
header: "bg-background-bright",
11+
cell: "group-hover/table-row:bg-charcoal-750 group-has-[[tabindex='0']:focus]/table-row:bg-charcoal-750",
12+
stickyCell: "bg-background-bright group-hover/table-row:bg-charcoal-750",
13+
menuButton:
14+
"bg-background-bright group-hover/table-row:bg-charcoal-750 group-hover/table-row:ring-charcoal-600/70 group-has-[[tabindex='0']:focus]/table-row:bg-charcoal-750",
15+
menuButtonDivider: "group-hover/table-row:border-charcoal-600/70",
16+
rowSelected: "bg-charcoal-750 group-hover/table-row:bg-charcoal-750",
17+
},
18+
dimmed: {
19+
header: "bg-background-dimmed",
20+
cell: "group-hover/table-row:bg-charcoal-800 group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
21+
stickyCell: "group-hover/table-row:bg-charcoal-800",
22+
menuButton:
23+
"bg-background-dimmed group-hover/table-row:bg-charcoal-800 group-hover/table-row:ring-grid-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
24+
menuButtonDivider: "group-hover/table-row:border-grid-dimmed",
25+
rowSelected: "bg-charcoal-750 group-hover/table-row:bg-charcoal-750",
26+
},
27+
} as const;
28+
29+
export type TableVariant = keyof typeof variants;
30+
831
type TableProps = {
932
containerClassName?: string;
1033
className?: string;
1134
children: ReactNode;
1235
fullWidth?: boolean;
1336
};
1437

15-
export const Table = forwardRef<HTMLTableElement, TableProps>(
16-
({ className, containerClassName, children, fullWidth }, ref) => {
38+
// Add TableContext
39+
const TableContext = createContext<{ variant: TableVariant }>({ variant: "dimmed" });
40+
41+
export const Table = forwardRef<HTMLTableElement, TableProps & { variant?: TableVariant }>(
42+
({ className, containerClassName, children, fullWidth, variant = "dimmed" }, ref) => {
1743
return (
18-
<div
19-
className={cn(
20-
"overflow-x-auto whitespace-nowrap border-t border-grid-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
21-
containerClassName,
22-
fullWidth && "w-full"
23-
)}
24-
>
25-
<table ref={ref} className={cn("w-full", className)}>
26-
{children}
27-
</table>
28-
</div>
44+
<TableContext.Provider value={{ variant }}>
45+
<div
46+
className={cn(
47+
"overflow-x-auto whitespace-nowrap border-t scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
48+
containerClassName,
49+
fullWidth && "w-full"
50+
)}
51+
>
52+
<table ref={ref} className={cn("w-full", className)}>
53+
{children}
54+
</table>
55+
</div>
56+
</TableContext.Provider>
2957
);
3058
}
3159
);
@@ -37,11 +65,13 @@ type TableHeaderProps = {
3765

3866
export const TableHeader = forwardRef<HTMLTableSectionElement, TableHeaderProps>(
3967
({ className, children }, ref) => {
68+
const { variant } = useContext(TableContext);
4069
return (
4170
<thead
4271
ref={ref}
4372
className={cn(
44-
"sticky top-0 z-10 bg-background-dimmed after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright",
73+
"sticky top-0 z-10 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright",
74+
variants[variant].header,
4575
className
4676
)}
4777
>
@@ -75,13 +105,14 @@ type TableRowProps = {
75105

76106
export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
77107
({ className, disabled, isSelected, children }, ref) => {
108+
const { variant } = useContext(TableContext);
78109
return (
79110
<tr
80111
ref={ref}
81112
className={cn(
82-
"group/table-row relative w-full after:absolute after:bottom-0 after:left-3 after:right-0 after:h-px after:bg-grid-dimmed",
113+
"group/table-row relative w-full outline-none after:absolute after:bottom-0 after:left-3 after:right-0 after:h-px after:bg-grid-dimmed",
114+
isSelected && variants[variant].rowSelected,
83115
disabled && "opacity-50",
84-
isSelected && isSelectedStyle,
85116
className
86117
)}
87118
>
@@ -94,7 +125,7 @@ export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
94125
type TableCellBasicProps = {
95126
className?: string;
96127
alignment?: "left" | "center" | "right";
97-
children: ReactNode;
128+
children?: ReactNode;
98129
colSpan?: number;
99130
};
100131

@@ -125,6 +156,7 @@ export const TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellP
125156
className
126157
)}
127158
colSpan={colSpan}
159+
tabIndex={-1}
128160
>
129161
{hiddenLabel ? (
130162
<span className="sr-only">{children}</span>
@@ -147,24 +179,12 @@ type TableCellProps = TableCellBasicProps & {
147179
hasAction?: boolean;
148180
isSticky?: boolean;
149181
actionClassName?: string;
150-
rowHoverStyle?: keyof typeof rowHoverStyles;
182+
rowHoverStyle?: string;
151183
isSelected?: boolean;
184+
isTabbableCell?: boolean;
185+
children?: ReactNode;
152186
};
153187

154-
const rowHoverStyles = {
155-
default:
156-
"group-hover/table-row:bg-charcoal-800 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-750 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-750 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3",
157-
dimmed:
158-
"group-hover/table-row:bg-charcoal-850 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-800 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-800 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3",
159-
bright:
160-
"group-hover/table-row:bg-charcoal-750 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-700 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-700 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3",
161-
};
162-
163-
const stickyStyles =
164-
"sticky right-0 bg-background-dimmed group-hover/table-row:bg-charcoal-750 w-[--sticky-width] [&:has(.group-hover\\/table-row\\:block)]:w-auto";
165-
166-
const isSelectedStyle = "bg-charcoal-750 group-hover:bg-charcoal-750";
167-
168188
export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
169189
(
170190
{
@@ -177,8 +197,8 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
177197
onClick,
178198
hasAction = false,
179199
isSticky = false,
180-
rowHoverStyle = "default",
181200
isSelected,
201+
isTabbableCell = false,
182202
},
183203
ref
184204
) => {
@@ -193,34 +213,47 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
193213
}
194214

195215
const flexClasses = cn(
196-
"flex w-full whitespace-nowrap px-3 py-3 text-xs text-text-dimmed",
216+
"flex w-full whitespace-nowrap px-3 py-3 items-center text-xs text-text-dimmed",
197217
alignment === "left"
198218
? "justify-start text-left"
199219
: alignment === "center"
200220
? "justify-center text-center"
201221
: "justify-end text-right"
202222
);
223+
const { variant } = useContext(TableContext);
203224

204225
return (
205226
<td
206227
ref={ref}
207228
className={cn(
208-
"text-xs text-charcoal-400",
209-
to || onClick || hasAction ? "cursor-pointer" : "px-3 py-3 align-middle",
229+
"text-xs text-charcoal-400 has-[[tabindex='0']:focus]:before:absolute has-[[tabindex='0']:focus]:before:-top-px has-[[tabindex='0']:focus]:before:left-0 has-[[tabindex='0']:focus]:before:h-px has-[[tabindex='0']:focus]:before:w-3 has-[[tabindex='0']:focus]:before:bg-grid-dimmed has-[[tabindex='0']:focus]:after:absolute has-[[tabindex='0']:focus]:after:bottom-0 has-[[tabindex='0']:focus]:after:left-0 has-[[tabindex='0']:focus]:after:right-0 has-[[tabindex='0']:focus]:after:h-px has-[[tabindex='0']:focus]:after:bg-grid-dimmed",
230+
variants[variant].cell,
231+
to || onClick || hasAction ? "cursor-pointer" : "cursor-default px-3 py-3 align-middle",
210232
!to && !onClick && alignmentClassName,
211-
isSticky && stickyStyles,
212-
isSelected && isSelectedStyle,
213-
!isSelected && rowHoverStyles[rowHoverStyle],
233+
isSticky &&
234+
"[&:has(.group-hover/table-row:block)]:w-auto sticky right-0 bg-background-dimmed",
235+
isSticky && variants[variant].stickyCell,
236+
isSelected && variants[variant].rowSelected,
237+
!isSelected &&
238+
"group-hover/table-row:before:absolute group-hover/table-row:before:left-0 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:before:bg-charcoal-750 group-hover/table-row:after:absolute group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3 group-hover/table-row:after:bg-charcoal-750 group-focus-visible/table-row:bg-background-bright",
214239
className
215240
)}
216241
colSpan={colSpan}
217242
>
218243
{to ? (
219-
<Link to={to} className={cn("focus-custom", flexClasses, actionClassName)}>
244+
<Link
245+
to={to}
246+
className={cn("cursor-pointer focus:outline-none", flexClasses, actionClassName)}
247+
tabIndex={isTabbableCell ? 0 : -1}
248+
>
220249
{children}
221250
</Link>
222251
) : onClick ? (
223-
<button onClick={onClick} className={cn("focus-custom", flexClasses, actionClassName)}>
252+
<button
253+
onClick={onClick}
254+
className={cn("cursor-pointer focus:outline-none", flexClasses, actionClassName)}
255+
tabIndex={isTabbableCell ? 0 : -1}
256+
>
224257
{children}
225258
</button>
226259
) : (
@@ -258,7 +291,7 @@ export const TableCellChevron = forwardRef<
258291

259292
export const TableCellMenu = forwardRef<
260293
HTMLTableCellElement,
261-
{
294+
TableCellProps & {
262295
className?: string;
263296
isSticky?: boolean;
264297
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
@@ -283,6 +316,7 @@ export const TableCellMenu = forwardRef<
283316
ref
284317
) => {
285318
const [isOpen, setIsOpen] = useState(false);
319+
const { variant } = useContext(TableContext);
286320

287321
return (
288322
<TableCell
@@ -297,15 +331,18 @@ export const TableCellMenu = forwardRef<
297331
<div className="relative h-full p-1">
298332
<div
299333
className={cn(
300-
"absolute right-0 top-1/2 mr-1 flex -translate-y-1/2 items-center justify-end gap-0.5 rounded-[0.25rem] bg-background-dimmed p-0.5 group-hover/table-row:bg-background-bright group-hover/table-row:ring-1 group-hover/table-row:ring-grid-bright",
301-
isSelected && isSelectedStyle,
302-
isSelected &&
303-
"group-hover/table-row:bg-charcoal-750 group-hover/table-row:ring-charcoal-600/50"
334+
"absolute right-0 top-1/2 mr-1 flex -translate-y-1/2 items-center justify-end gap-0.5 rounded-[0.25rem] p-0.5 group-hover/table-row:ring-1",
335+
variants[variant].menuButton
304336
)}
305337
>
306338
{/* Hidden buttons that show on hover */}
307339
{hiddenButtons && (
308-
<div className="hidden pr-0.5 group-hover/table-row:block group-hover/table-row:border-r group-hover/table-row:border-grid-dimmed">
340+
<div
341+
className={cn(
342+
"hidden pr-0.5 group-hover/table-row:block group-hover/table-row:border-r",
343+
variants[variant].menuButtonDivider
344+
)}
345+
>
309346
{hiddenButtons}
310347
</div>
311348
)}

apps/webapp/app/components/primitives/TextLink.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { cn } from "~/utils/cn";
44

55
const variations = {
66
primary:
7-
"text-indigo-500 transition hover:text-indigo-400 inline-flex gap-0.5 items-center group",
7+
"text-indigo-500 transition hover:text-indigo-400 inline-flex gap-0.5 items-center group focus-visible:focus-custom",
88
secondary:
9-
"text-text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group",
9+
"text-text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group focus-visible:focus-custom",
1010
} as const;
1111

1212
type TextLinkProps = {
@@ -34,14 +34,14 @@ export function TextLink({
3434
<Link to={to} className={cn(classes, className)} {...props}>
3535
{children}{" "}
3636
{trailingIcon && (
37-
<NamedIcon name={trailingIcon} className={cn("h-4 w-4", trailingIconClassName)} />
37+
<NamedIcon name={trailingIcon} className={cn("size-4", trailingIconClassName)} />
3838
)}
3939
</Link>
4040
) : href ? (
4141
<a href={href} className={cn(classes, className)} {...props}>
4242
{children}{" "}
4343
{trailingIcon && (
44-
<NamedIcon name={trailingIcon} className={cn("h-4 w-4", trailingIconClassName)} />
44+
<NamedIcon name={trailingIcon} className={cn("size-4", trailingIconClassName)} />
4545
)}
4646
</a>
4747
) : (

apps/webapp/app/components/runs/v3/RunTag.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function RunTag({ tag }: { tag: string }) {
2222
<span className="flex items-center border-y border-r border-charcoal-700 bg-charcoal-800 pr-1.5 text-text-dimmed">
2323
{tagResult.key}
2424
</span>
25-
<span className="flex items-center rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-750 px-1.5 text-text-dimmed">
25+
<span className="flex items-center whitespace-nowrap rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-750 px-1.5 text-text-dimmed">
2626
{tagResult.value}
2727
</span>
2828
</span>

apps/webapp/app/components/runs/v3/ScheduleFilters.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function ScheduleFilters({ possibleEnvironments, possibleTasks }: Schedul
9393
}, []);
9494

9595
return (
96-
<div className="flex w-full flex-row">
96+
<div className="flex w-full">
9797
<Input
9898
name="search"
9999
placeholder="Search schedule id, external id, deduplication id or CRON pattern"
@@ -103,7 +103,7 @@ export function ScheduleFilters({ possibleEnvironments, possibleTasks }: Schedul
103103
defaultValue={search}
104104
onChange={(e) => handleSearchChange(e.target.value)}
105105
/>
106-
<SelectGroup>
106+
<SelectGroup className="ml-2">
107107
<Select name="type" value={type ?? "ALL"} onValueChange={handleTypeChange}>
108108
<SelectTrigger size="minimal" width="full">
109109
<SelectValue placeholder={"Select type"} className="ml-2 whitespace-nowrap p-0" />

0 commit comments

Comments
 (0)