All files / src/components/ui separator.tsx

100% Statements 3/3
100% Branches 14/14
100% Functions 1/1
100% Lines 3/3

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72                                                                119x                         3608x                                             119x      
import { forwardRef, type HTMLAttributes } from "react";
import { cn } from "../../lib/utils";
 
/**
 * A hairline divider. **Dividers are hairlines, not boxes** — the Job Book's
 * one rule for separating things (see `.hairline` / `.rule` in
 * `.context/mockups/_base.css` §7.1).
 *
 * - `strong` picks `border-strong` instead of `border-default`: the heavier
 *   rule that sits under a column head or above a totals strip.
 * - `marked` is the *page rule*: the long hairline under a page title carrying
 *   the one 3px index-red inset bar on that screen. **Use it once per page**
 *   and a plain separator everywhere else — a filled index-red block, or a
 *   second marked rule, spends the marker's scarcity. Horizontal only.
 * - Decorative by default (`role="none"`, like Radix's Separator): a rule that
 *   only groups things visually must not be announced. Pass
 *   `decorative={false}` when the rule itself carries meaning, and it becomes
 *   a real `role="separator"`.
 *
 * The marker ink is `secondary-600`, which *is* index red — see the note in
 * `table.tsx` for why that spelling and not `index`.
 */
export interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {
	orientation?: "horizontal" | "vertical";
	/** Default true — a purely visual rule is not announced. */
	decorative?: boolean;
	/** The heavier rule: `border-strong` instead of `border-default`. */
	strong?: boolean;
	/** The page rule's 3px index-red inset bar. Once per page, horizontal only. */
	marked?: boolean;
}
 
const Separator = forwardRef<HTMLDivElement, SeparatorProps>(
	(
		{
			className,
			orientation = "horizontal",
			decorative = true,
			strong,
			marked,
			...props
		},
		ref,
	) => (
		// biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-orientation is only ever set on the role="separator" branch, which supports it; biome cannot follow the conditional role
		<div
			ref={ref}
			role={decorative ? "none" : "separator"}
			// `horizontal` is the default for role="separator", so it is only
			// worth stating for a vertical one.
			aria-orientation={
				!decorative && orientation === "vertical" ? "vertical" : undefined
			}
			data-orientation={orientation}
			className={cn(
				"shrink-0",
				orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
				strong ? "bg-border-strong" : "bg-border-default",
				// The marker is 3px and only ever 3px (--mark-w), 2.5rem long,
				// sitting on the rule rather than filling it.
				marked &&
					"relative before:absolute before:top-1/2 before:left-0 before:h-[3px] before:w-10 before:-translate-y-1/2 before:bg-secondary-600 before:content-['']",
				className,
			)}
			{...props}
		/>
	),
);
Separator.displayName = "Separator";
 
export { Separator };