Projects
betaA projects page with board, list, and timeline layouts that share one shell per project.
Built from Badge, Card, Progress, Ring Progress, Search Field, Select, Skeleton, Tooltip, View morph, and View switch.
Preview
Projects
14 projects
- To do
- In progress
- In review
- Done
List
Projects
14 projects
| Project | Priority | Progress | Roles | Due |
|---|---|---|---|---|
To do | ||||
| Nov 18 | ||||
| Nov 21 | ||||
| Dec 2 | ||||
| Dec 2 | ||||
In progress | ||||
| Nov 3 | ||||
| Nov 12 | ||||
| Nov 20 | ||||
In review | ||||
| Oct 31 | ||||
| Nov 5 | ||||
Done | ||||
| Oct 22 | ||||
| Oct 15 | ||||
| Oct 24 | ||||
| Oct 17 | ||||
| Oct 27 | ||||
Timeline
Projects
14 projects
The date line marks a fixed example, Oct 27, 2026.
- Nov 18
- Nov 21
- Dec 2
- Dec 2
- Nov 3
- Nov 12
- Nov 20
- Oct 31
- Nov 5
- Oct 22
- Oct 15
- Oct 24
- Oct 17
- Oct 27
Loading board
Projects
Loading projects
Loading list
Projects
Loading projects
Loading timeline
Projects
Loading projects
Empty page
Projects
No projects yet.
No projects yet.
Empty group
Projects
12 projects
- To do
- In progress
- In review
Nothing in In review.
- Done
Nothing matches
Projects
No projects match.
No projects match.
Overdue
Projects
14 projects
| Project | Priority | Progress | Roles | Due |
|---|---|---|---|---|
To do | ||||
| Nov 18 | ||||
| Nov 21 | ||||
| Dec 2 | ||||
| Dec 2 | ||||
In progress | ||||
| Overdue Nov 3 | ||||
| Nov 12 | ||||
| Nov 20 | ||||
In review | ||||
| Overdue Oct 31 | ||||
| Overdue Nov 5 | ||||
Done | ||||
| Oct 22 | ||||
| Oct 15 | ||||
| Oct 24 | ||||
| Oct 17 | ||||
| Oct 27 | ||||
Filtered
Projects
4 of 14
- To do
- In progress
- In review
- Done
Could not load
Projects
Projects could not be loaded. Check the connection and try again.
Projects could not be loaded. Check the connection and try again.
Long names
Projects
1 project
- To do
Nothing in To do.
- In progress
- In review
Nothing in In review.
- Done
Nothing in Done.
Projects
1 project
| Project | Priority | Progress | Roles | Due |
|---|---|---|---|---|
To do | ||||
| Nothing in To do. | ||||
In progress | ||||
| Dec 1 | ||||
In review | ||||
| Nothing in In review. | ||||
Done | ||||
| Nothing in Done. | ||||
Zoom 400
Projects
1 project
| Project | Priority | Progress | Roles | Due |
|---|---|---|---|---|
To do | ||||
| Nov 18 | ||||
In progress | ||||
| Nothing in In progress. | ||||
In review | ||||
| Nothing in In review. | ||||
Done | ||||
| Nothing in Done. | ||||
Narrow board
Projects
14 projects
- To do
- In progress
- In review
- Done
Narrow timeline
Projects
14 projects
The date line marks a fixed example, Oct 27, 2026.
- Nov 18
- Nov 21
- Dec 2
- Dec 2
- Nov 3
- Nov 12
- Nov 20
- Oct 31
- Nov 5
- Oct 22
- Oct 15
- Oct 24
- Oct 17
- Oct 27
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the template. It also adds every component it uses. Its own files go in your
componentsfolder, and they are yours to edit.npx shadcn@latest add @txbrendan/projects
Use it for
- A page of projects someone switches between a board, a list, and a timeline.
- Pass projects, and a today date, when the records are yours. The defaults are a worked example, and the page says so.
- Set embedded when the page sits inside another page, so it does not add a second main.
- Set loading while the records are on the way. The skeleton matches the layout. An empty projects array is the empty page. A search or priority that matches nothing says so, and a filter that matches some says how many of the total.
- Set error to a sentence that says what to do next, when the records could not be loaded.
Avoid it for
- A table you sort and page. The list is one of three layouts of the same shells, not a data table.
- A second accent color. Orange is only the high pill. Red is only the date line. Timeline bars share one neutral fill.
Props
- projects
The records. Default is a worked example.
Project[]
- today
The date the page treats as today, YYYY-MM-DD. Omit it and the page uses 2026-10-27 and says that date is a fixed example. A project due before the date, and not done, is overdue.
string
- loading
Shows a skeleton in the current layout's shape instead of the records.
boolean · default false
- error
Replaces the records with this message. Say what to do next.
string
- embedded
Renders a div instead of a main, for a preview inside another page.
boolean · default false
- titleLevel
Heading level of the title. Default h1, or h2 when embedded.
"h1" | "h2"
- defaultLayout
The layout on first paint, when layout is not set.
"board" | "list" | "timeline" · default "board"
- layout
The layout that is showing. Use with onLayoutChange.
"board" | "list" | "timeline"
- onLayoutChange
Called with the layout that was just chosen.
(layout: "board" | "list" | "timeline") => void
- defaultQuery
The search box on first paint.
string · default ""
- defaultPriority
The priority filter on first paint.
"all" | "urgent" | "high" | "medium" | "low" · default "all"
- interactive
When false, the project controls leave the tab order. The layout switch stays.
boolean · default true
- className
Classes on the root, which holds the container query.
string
- Part. Project
One project. id is stable across layouts. start and due are YYYY-MM-DD. roles are job names, such as Lead, not people.
{ id: string; name: string; client: string; summary: string; stage: "todo" | "progress" | "review" | "done"; priority: "urgent" | "high" | "medium" | "low"; done: number; total: number; start: string; due: string; roles: string[] }
| Prop | Type | Default | What it does |
|---|---|---|---|
| projects | Project[] | - | The records. Default is a worked example. |
| today | string | - | The date the page treats as today, YYYY-MM-DD. Omit it and the page uses 2026-10-27 and says that date is a fixed example. A project due before the date, and not done, is overdue. |
| loading | boolean | false | Shows a skeleton in the current layout's shape instead of the records. |
| error | string | - | Replaces the records with this message. Say what to do next. |
| embedded | boolean | false | Renders a div instead of a main, for a preview inside another page. |
| titleLevel | "h1" | "h2" | - | Heading level of the title. Default h1, or h2 when embedded. |
| defaultLayout | "board" | "list" | "timeline" | "board" | The layout on first paint, when layout is not set. |
| layout | "board" | "list" | "timeline" | - | The layout that is showing. Use with onLayoutChange. |
| onLayoutChange | (layout: "board" | "list" | "timeline") => void | - | Called with the layout that was just chosen. |
| defaultQuery | string | "" | The search box on first paint. |
| defaultPriority | "all" | "urgent" | "high" | "medium" | "low" | "all" | The priority filter on first paint. |
| interactive | boolean | true | When false, the project controls leave the tab order. The layout switch stays. |
| className | string | - | Classes on the root, which holds the container query. |
| Part. Project | { id: string; name: string; client: string; summary: string; stage: "todo" | "progress" | "review" | "done"; priority: "urgent" | "high" | "medium" | "low"; done: number; total: number; start: string; due: string; roles: string[] } | - | One project. id is stable across layouts. start and due are YYYY-MM-DD. roles are job names, such as Lead, not people. |
Accessibility
- The page is a main, unless embedded. The title is an h1, or an h2 when embedded, unless titleLevel says otherwise.
- The layout control is a tab list. The morph is the tab panel, named by the active tab.
- The board is a list of lists: one list named Board, and a list named for each stage. The list is a table. The timeline groups its rows, and each group is a list named for its stage.
- Each project has one button, named with the project, the client, and the priority. The card, the row, and the timeline name are that button. The priority pill is named with its level. A tooltip carries the summary, the priority, the roles, and the task count. Enter or Space selects it. A layout change that drops focus puts it back on that button.
- Set interactive to false to take those buttons out of the tab order. The layout switch stays in it.
- A timeline bar's text equivalent is the name, the priority, the start, the due date, and the percent. The row shows that priority once, on the pill.
- A status outside the busy region says when the page is loading, empty, or filtered to nothing.
- The move follows the motion tokens. Motion off and reduced motion do not translate the shells.
Examples
The worked example
<Projects />Inside another page
<Projects embedded interactive={false} titleLevel="h1" />