Today cards
NewAn assistant’s new-session screen that opens on your day: a greeting over the composer, rotating suggestions, and four cards for meetings, tasks, pipelines and numbers.
today-cards/today-cards.tsxAsk me about a deal, Marta
Tue, Oct 13
Upcoming meetings
Tasks due
Top pipelines
Recent performance
Meet Sevenfold Routines™
Routines are saved tasks Sevenfold runs on demand, like a morning brief, meeting prep, or a pipeline review.
- Morning briefWeekdays at 8:30 AM
- Meeting prepAn hour before each call
- Pipeline reviewMondays at 9:00 AM
The live component, numbered part by part. Every measurement is read from it as it is drawn. Point at a row to outline its part, or at a part to find its row.
When to use
- The empty state of an assistant or chat product: a new session that opens on the reader’s day instead of a blank box.
- When a few things from other tools (meetings, tasks, pipelines, figures) should be one click from a question about them.
- Rotating suggestions that teach what the assistant can do, with the greeting and placeholder changing to match.
- Cards that stay calm until pointed at: actions, tooltips and “what a click does” only on hover or focus.
- A screen that must work in a wide window, a narrow panel and a phone: it lays out by its own width, not the window’s.
When not to use
- A full dashboard to read figures over time. Four cards of three rows each are a glance; use charts and tables instead.
- Long lists of meetings or tasks. Each card shows three; the Browse palette is for jumping, not for managing a backlog.
- A product with no assistant or composer: without a question to ask, the cards are just widgets. Use a workspace home.
- A screen read mostly by screen reader or keyboard in a hurry: the hover affordances add steps. Keep plain links there.
- When the suggestions’ motion would distract from work under way. Turn
autoplayoff, or don’t open on this screen.
You can add this component with the shadcn CLI or by hand:
Into components/today-cards of any React project, with no setup:
npx shadcn@latest add https://brunodelorence.com/r/today-cards.json- Copy the component files
Copy these files into one folder of your project, keeping their relative imports together, or download them as a ZIP:
- Import it
As shown under Usage, below.
Either way it needs React 19.2 and a bundler that supports CSS Modules, such as Next.js or Vite. It has a dark colour scheme: it follows your page’s color-scheme, light unless the page sets one, or its colorScheme prop, with nothing to configure. Browsers without light-dark() show it light.
Usage
import { TodayCards } from './today-cards';export function NewSession() { return <TodayCards name="Marta" defaultSuggestion="recap" />;}Each state below is a live instance, locked so you can study it.
The pieces it is built from, each with its measurements read live.
The home screen of an invented sales assistant. The greeting changes with the suggestion picked, word by word, as the row of suggestions slides to centre it. Under the composer, the date and four cards: upcoming meetings that count down, tasks to tick (with a toast to undo and the next task sliding in), the busiest pipelines and the last 30 days’ numbers. Each card has its actions on hover: a Browse palette with search and groups, a menu whose Ask item opens questions in a submenu. A side card announces the cards and opens a release note.
- The suggestions are a loop drawn three times over: the row is translated so the picked chip’s centre meets the view’s, on a 0.48 s ease-out. When the pick leaves the middle copy, the row jumps back to the same chip in it with its transition off for one frame, so the loop never ends. On its own it moves on every 7 s while on screen, and stops while a palette, the note or a draft is open.
- The greeting is a list of words, the name one of them in grey. A new pick keeps the old line for 180 ms as its words blur out, while the new words come in one after another (55 ms apart) from a blur and 4 px lower. The composer’s placeholder changes with it.
- A ticked task is crossed out at once and a toast offers Undo. After 1.6 s its row blurs away; the rows under it slide up into its place (FLIP, by the Web Animations API, measured in a layout effect) and the next task due fades in at the bottom. Undo cancels the timers and puts it back where it was.
- Each card shows add, Browse and More on hover or focus, with a tooltip that waits 350 ms. Browse opens a palette over a dimmed screen: a search, groups, the arrow keys and a footer button that says what Enter will do (Join meeting, Open, Open pipeline). More opens a menu whose Ask item opens three questions in a submenu; Recent performance asks from its own list. A question goes into the composer.
- Rows say what a click does (Join meeting, Open task, Open pipeline, Ask Sevenfold AI) over a fade on hover, in place of their figures.
- Menus, the palette, the note and the toast are drawn at the screen’s root and placed from their button’s box in the screen’s own px, so they are right when the screen is drawn scaled. Each enters by opacity, a 3 px blur and a 3% scale, and leaves the same way in 140 ms.
- The rainbow across the top is one SVG: seven strokes along the same curve, blurred once by an SVG filter and masked at both ends, over a soft grey haze. The side card’s picture is the meetings card, drawn again with a slow drift of three radial gradients behind it.
- Layout is by container queries on the root: the sidebar becomes a drawer under 900 px, and under 640 px the cards stack, the greeting shrinks and a submenu opens under its menu.
- While on screen and untouched, a demo pointer opens the release note and scrolls it, browses meetings, ticks two tasks, asks Recent performance, browses pipelines and opens two menus with their questions, then puts the tasks back. Any real input hands it over; it comes back after 8 s of quiet.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | 'Marta' | The reader’s first name, in grey in the greeting. |
defaultSuggestion | 'apps' | 'csv' | 'deal' | 'follow' | 'recap' | 'changed' | 'routine' | 'deal' | The suggestion picked first, which sets the first greeting and placeholder. |
autoplay | boolean | true | While on screen and untouched, the suggestions rotate and a demo pointer shows the note, the palettes, the tasks and the menus. Real input hands control to the reader. |
colorScheme | 'light' | 'dark' | 'system' | — | Light or dark colours, or the reader’s system setting; without it, the page’s color-scheme. |
defaultState | TodayCardsState | — | Opens held in a state, with the demo and the rotation off: { palette?: 'meetings' | 'tasks' | 'pipelines'; menu?: card id; pick?, subPick?: the item drawn active; article?: boolean; promo?: boolean; done?, gone?: task ids ticked or ticked away; drawer?: boolean; text?: a draft; hover?: a card, row or button drawn as hovered }. |
onTaskToggle | (id: string, done: boolean) => void | — | Called when a task is ticked, or put back by Undo or a second click. |
onSuggestionChange | (id: string) => void | — | Called when another suggestion is picked, by the reader or the rotation. |
onSend | (text: string) => void | — | Called with the message when the composer is sent. |
className | string | — | Extra class for the root. |
The complete source behind this demo. Open source under the MIT license, © 2026 Bruno Delorence: copy it, change it, ship it.
// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import { useEffect, useEffectEvent, useLayoutEffect, useRef, useState, useSyncExternalStore, type CSSProperties, type ReactNode,} from 'react';import { ASK, DATE_LABEL, MEETINGS, METRICS, PERFORMANCE_QUESTIONS, PIPELINES, PRODUCT, ROUTINES, SESSIONS,// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode, type Ref,} from 'react';import { ARTICLE, MEETINGS, PIPELINES, TASKS, type Meeting, type Pipeline, type Task,} from './data';import { DayTile, Icon, Mark, PipelineTile, type IconName } from './icons';import styles from './today-cards.module.css';// Copyright (c) 2026 Bruno Delorence. MIT License./* * The invented workspace the screen shows: an assistant called Sevenfold, a sales team's day on * Tuesday 13 October, its meetings, tasks, pipelines and numbers, the composer's suggestions and * the release note the side card links to. Every company and person here is made up. */export const PRODUCT = 'Sevenfold';export const WORKSPACE = 'Hollowell Demo';export const DATE_LABEL = 'Tue, Oct 13';export type PipelineIcon = 'case' | 'branch' | 'swap' | 'people' | 'stack';/** A pipeline's tile: a gradient square with a white glyph. */export type Pipeline = { id: string; name: string; /** Two colours, top left to bottom right. */ tint: [string, string]; icon: PipelineIcon; deals?: number; /** In dollars. */ value?: number;// Copyright (c) 2026 Bruno Delorence. MIT License.import type { CSSProperties } from 'react';import type { ChipIcon, Pipeline, PipelineIcon } from './data';/* * The screen's drawings: line icons on a 16-unit grid in currentColor (their size comes from the * stylesheet), the assistant's seven-sided mark, the pipelines' gradient tiles, the calendar * tile, the progress ring and the invented app marks of the composer's tray. All decorative: * whatever they stand for is said in text beside them. */export type IconName = | 'plus' | 'search' | 'panel' | 'review' | 'book' | 'gear' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'caret-down' | 'caret-right'// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import type { CSSProperties, Ref } from 'react';/* * The demo's pointer, and the steps that move it. A demo is a script of awaited steps: wait, or * glide the pointer somewhere. Stopping the demo (the reader takes over, or it scrolls away) ends * the step in progress at once: its promise rejects, which ends the script right there, and the * pointer fades out. * * Each glide is a curved flight played by the Web Animations API, so nothing runs frame by frame. * The points of the flight are handed to whoever wants to move along with it (nothing here * does, but the hook is kept). The arrow and the hand are HTML boxes * that swap by their visibility: a change of style inside an SVG would lay the SVG out again. */export type Point = { x: number; y: number };/** A glide's path and timing, for an animation that plays along with it. */export type Flight = { points: Point[]; timing: KeyframeAnimationOptions };export type Demo = {// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import { useCallback, useEffect, useRef, useState } from 'react';/* * When a demo may play on its own: while part of it is on screen and the tab is visible, never * under reduced motion, and not while the reader is using it. `take()` hands it to the reader; it * takes back over `idle` ms after their last input. */export function useAutoplay<T extends HTMLElement>(idle = 6000) { const ref = useRef<T>(null); const [visible, setVisible] = useState(false); const [reduced, setReduced] = useState(true); const [taken, setTaken] = useState(false); const timer = useRef(0); useEffect(() => { const element = ref.current; if (!element) return; let onScreen = false; const update = () => setVisible(onScreen && !document.hidden); const observer = new IntersectionObserver(/* Today cards (today-cards.tsx): an assistant's new-session screen. Near-white, with a pale warm grey sidebar, a soft rainbow across the top, white composer and pale grey cards; orange for meetings on now, crimson for late tasks, green for numbers that moved the right way. At night near-black, the cards a step lighter, the rainbow dimmed. Colours are the tokens below, light-dark(light, dark) where the two differ: the screen takes the color-scheme it inherits from the page, or the one its colorScheme prop sets (data-color-scheme). Layout is by container queries on the root: a 215 px sidebar from 900 px, a drawer behind a menu button under that, the cards in one column under 640 px. Memory. Only transforms, opacity and a small blur animate (menus, the palette, the toast, the greeting's words, the suggestions' row); the rainbow is one static SVG, blurred once. */.root { --bg: #fdfcfc; --side: #f8f7f7; --side-line: #efeded; --ink: #1d1d1d; --ink-2: #3a3a3a; --muted: #8b8b8b; --muted-2: #a3a3a3; --faint: #bdbdbd;- Landmarks and headings: the sidebar is an aside with a nav, the screen a main whose h1 is the greeting (its full sentence, for screen readers, while the animated words are hidden from them); the day and each card are sections named by their headings.
- The suggestions are a group of toggle buttons (aria-pressed) with previous and next buttons; only the real copy of the loop is in the tab order.
- Card actions are named buttons that also show on focus; More and Ask have aria-haspopup and aria-expanded. Menus are role="menu" with the arrow keys, ArrowRight or Enter into a submenu, ArrowLeft or Escape back, and the focus returns to the button.
- The Browse palette is a modal dialog whose search is a combobox over a listbox, with aria-activedescendant following the arrow keys; Enter runs the active item and Escape closes it.
- Each task’s box is a role="checkbox" named for the task; the toast is in a polite live region, with Undo as a real button.
- The release note is a modal dialog labelled by its title; it takes the focus on its close button and gives it back to Learn more.
- The demo pointer is aria-hidden, and under reduced motion nothing moves on its own: no rotation, no demo, no blurs or slides.