Fills & patterns
StableSolid, hatched, outline and dashed: the second “colour” of a black-and-white interface, plus the dot-grid canvas.
Where another system would reach for a hue, this one changes the fill. rc-hatch draws 1px diagonal lines in the current text colour; the calendar’s four patterns tell its calendars apart by fill alone, so they read the same in greyscale and in print. rc-canvas is the faint dot grid behind previews and empty boards.
The four fills
import { PatternFill, PatternSwatch, patternText } from '@rchursin/product/calendar/patterns'import { SampleBadge } from '@rchursin/product/shell/layout'
/** * Four calendars told apart by fill, never by colour: solid, hatched, outline and dashed. * They read the same in greyscale, in print and for people who can't tell colours apart. */const events = [ { calendar: 'Team', pattern: 'solid', title: 'Design review', time: '10:00 – 11:00' }, { calendar: 'Focus', pattern: 'hatched', title: 'Deep work', time: '13:00 – 15:00' }, { calendar: 'Personal', pattern: 'outline', title: 'Dentist', time: '17:30 – 18:15' }, { calendar: 'Releases', pattern: 'dashed', title: 'Library release', time: 'All day' },] as const
export default function FillPatterns() { return ( <div className="@container flex w-full max-w-3xl flex-col gap-5"> <div className="flex flex-wrap items-center justify-between gap-3"> <ul aria-label="Calendars" className="flex flex-wrap gap-x-4 gap-y-2"> {events.map((e) => (Usage
import '@rchursin/product.css' // brand tokens, fonts, the finishing layer and the app tokensimport { PatternFill, PatternSwatch, patternText } from '@rchursin/product/calendar/patterns'<span className="rc-hatch size-5 rounded-[3px] text-fg" />
<div className="relative overflow-hidden rounded-lg p-3"> <PatternFill pattern="hatched" /> <span className={`relative ${patternText.hatched.main}`}>Deep work</span></div>Examples
Hatching
rc-hatch takes the text colour, so text-* sets its strength. It marks things in between: in progress, planned.
/** * rc-hatch draws 1px diagonal lines in the current text colour, so text-* sets how strong it is. * Use it for "in between" states: in progress, planned, partly done. */export default function Hatch() { return ( <div className="@container w-full max-w-3xl"> <div className="grid gap-8 @2xl:grid-cols-3"> <figure className="flex flex-col gap-3"> <div className="flex gap-1.5" role="img" aria-label="Focus sets: two done, one in progress, one to go"> <span className="size-5 rounded-[3px] bg-fg" /> <span className="size-5 rounded-[3px] bg-fg" /> <span className="rc-hatch size-5 rounded-[3px] text-fg shadow-[inset_0_0_0_1px_currentColor]" /> <span className="size-5 rounded-[3px] shadow-[inset_0_0_0_1px_var(--rc-line-strong)]" /> </div> <figcaption className="text-[12.5px]/5 text-pretty text-fg-3"> <span className="font-medium text-fg">Progress.</span> Done is solid, in progress is hatched, the rest are outlines. </figcaption> </figure>
Canvas
The dot grid on an empty column. The Library’s preview frames use it too.
import { Icon } from '@rchursin/product/icons/Icon'
/** * rc-canvas: a faint 16px dot grid, like a drafting table. The Library's preview frames use it; * it also makes an empty board column feel like a place to put things. */export default function Canvas() { return ( <div className="grid w-full max-w-md grid-cols-2 gap-3"> {[ { name: 'In progress', count: 2 }, { name: 'In review', count: 0 }, ].map((col) => ( <div key={col.name} className="flex min-h-56 flex-col overflow-hidden rounded-2xl bg-sunken ring-1 ring-line"> <h3 className="flex items-center justify-between border-b border-line px-3 py-2.5 text-[12.5px] font-medium text-fg"> {col.name} <span className="font-mono text-[11px] text-fg-3 tabular-nums">{col.count}</span> </h3> {col.count > 0 ? ( <ul className="flex flex-col gap-2 p-2"> {['Draw the month view', 'Check focus rings'].map((t) => ( <li key={t} className="rounded-lg bg-raised px-3 py-2.5 text-[12.5px] text-fg shadow-[var(--lux-shadow-1)]"> {t} </li> ))} </ul> ) : ( <div className="rc-canvas flex flex-1 flex-col items-center justify-center gap-2 p-4 text-center"> <Icon name="square-dashed" className="size-5 text-fg-3" /> <p className="text-[12px] text-pretty text-fg-3">Nothing in review yet</p> </div> )} </div> ))} </div> )}API
PatternFill props
| Prop | Type | Default | Description |
|---|---|---|---|
patternrequired | 'solid' | 'outline' | 'dashed' | 'hatched' | – | Which fill to draw. Place it first inside a relative, overflow-hidden element. |
bar | boolean | true | The 2px bar on the left edge (dashed for the dashed fill). |
patternText
| Prop | Type | Default | Description |
|---|---|---|---|
patternText[pattern] | { main: string; meta: string } | – | Text classes that read on each fill: on-inverse on solid, fg and fg-2 on the rest. Put them on content with position relative so it sits above the fill. |
CSS
| Class or variable | What it does |
|---|---|
rc-hatch | repeating-linear-gradient(−45deg): a 1px line every 5px in currentColor. Set the strength with text-fg, text-fg-3 or text-fg/[0.14]. |
rc-canvas | A 1px dot every 16px in fg at 9%, mixed so it works in both themes. |
Outline | An inset 1–1.5px box-shadow in fg: shadow-[inset_0_0_0_1.5px_var(--rc-fg)]. |
Dashed | border border-dashed border-fg (or border-line-strong for empty slots and placeholders). |
Accessibility
- The patterns never carry meaning alone: event blocks name their calendar in text, and PatternFill and PatternSwatch are aria-hidden.
- Each fill is distinct in greyscale and when printed, which colour-coded calendars are not.
- Text on the solid fill uses on-inverse; on the others fg, both at 4.5:1 or more.
Guidelines
Do
- Keep the meanings steady: solid is done or committed, hatched is in progress or tentative, outline is open, dashed is a placeholder or an external thing.
- Use a soft hatch (text-fg/[0.14]) behind text and a full one for small keys.
- Name the thing in words next to the fill.
Don’t
- Don’t use more than four fills in one view.
- Don’t put a hatch behind small text at full strength.
- Don’t add hues to tell series apart.
In use
Used on this site
Related
- Event blocksHow an event looks in each calendar view: a block in the week grid, a bar for all-day, a chip in a month cell.
- CalendarThe whole calendar: week, day, month and agenda views, calendars told apart by pattern, and create, edit and delete.
- Progress squareProgress drawn as the brand’s square, closing clockwise from the top centre, and a row of squares for a set of focus sessions.
- ColourTwelve semantic greys that switch with the theme, and six fixed brand values for the logo and brand panels.
Source
The component’s own files, exactly as they run on this site.
/* RChursin_ product kit: app-level tokens and details on top of brand.css, effects.css and luxe.css. Import through src/product.css (which also pulls in the brand layer).
Semantic colours become Tailwind utilities: text-fg, text-fg-2, text-fg-3, bg-surface, bg-raised, bg-sunken, border-line, ring-line-strong, bg-hover, bg-selected … They switch with the .dark class, so components never need dark: variants for these.*/
@theme inline { --color-fg: var(--rc-fg); --color-fg-2: var(--rc-fg-2); --color-fg-3: var(--rc-fg-3); --color-surface: var(--rc-surface); --color-raised: var(--rc-raised); --color-sunken: var(--rc-sunken); --color-line: var(--rc-line); --color-line-strong: var(--rc-line-strong); --color-hover: var(--rc-hover); --color-selected: var(--rc-selected);