About Page
betaA whole about page, built from our website components: a floating navbar, a page title, the story beside its headline, a few figures, what you hold to, the people, a closing band, and a footer.
Preview
We build the app we wanted for our own teams.
A small company that makes one product for the people who run teams.
Why we started.
We ran teams for years with a spreadsheet, a shared calendar, and a chat that never stopped. Every week someone missed the change of time.
So we built one place for who is coming, what is planned, and how each goal is going. Our own teams used it first.
We still answer every message ourselves.
Where it stands.
12
People on the team
Across 6 countries
3
Offices
In three time zones
2019
The year we started
What we hold to.
Plain words
Every screen says what it does in words a new member understands.
Your data is yours
Anyone can download what we hold about them, or ask us to delete it.
Fast on every phone
Each page loads quickly on a slow network and an old phone.
The people who build it.
Plan your team’s week in one place.
Free for one team. No card needed.
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/about-page
Use it for
- The about page of a product site. Render it as the whole page, from your app's page file.
- Write every word in the props: one object for the navbar, the header, the story, the values, the team, the closing band, and the footer.
- Keep the story to two to four short paragraphs: why you started, what you built, and where it stands.
- Use `stats` for two to four figures that are true today, like the size of the team or the year you started.
- Use `values` for what you hold to, two to six, each a short title and one line.
- Use `team` for the people, and set `team.team.openRoles` to end the grid with a link to open roles.
- Use `cta` for the one thing to do next, like signing up or seeing open roles.
- Pass your router's link once in `renderLink`. The navbar, the team, the closing band, and the footer all use it. From a server page, render the page from a client file, since a server page cannot pass a function.
- Set `embedded` when you show the page inside another page, like a gallery, so it has no second main.
Avoid it for
- A history told year by year. Tell it in a few lines, and link to a changelog for the rest.
- Figures that will be out of date next month, like a count of signups, unless you update the page.
- Values no one would disagree with, like quality. Name what you do, like answering every message.
- A team grid of more than about sixteen people. Show the leads, and link to the rest.
Props
- navbar
The Navbar's props. It floats as a pill once the page scrolls, and folds into a menu below 42rem.
NavbarProps
- header
The headline at the top of the page. It renders as the page's one h1.
HeadlineProps
- story
Why you started, as a headline beside a few short paragraphs. They stack below 64rem. Its id defaults to "story".
{ id?: string; headline: HeadlineProps; paragraphs: ReactNode[] }
- stats
Two to four figures, as a Stat Band, each with a `value`, a `label`, and an optional `note`. Its id defaults to "numbers". With no headline, `label` names it for screen readers, default "Numbers". With no items, it is left out.
{ id?: string; headline?: HeadlineProps; label?: string; items: StatBandItem[] }
- values
What you hold to, one Feature Grid cell each. Its id defaults to "values". With no headline, `label` names it for screen readers, default "Values". With no items, it is left out.
{ id?: string; headline?: HeadlineProps; label?: string; items: FeatureGridItemProps[] }
- team
The people, as a Team Grid. Its id defaults to "team". With no headline, `label` names it for screen readers, default "Team". With no people and no open roles, it is left out.
{ id?: string; headline?: HeadlineProps; label?: string; team: TeamGridProps }
- cta
The closing band above the footer.
CallToActionBandProps
- footer
The Footer's props.
FooterProps
- renderLink
Draws every link in the navbar, the team, the closing band, and the footer, unless a part sets its own.
(props: NavbarLinkProps) => ReactNode · default a plain <a>
- embedded
Renders the content in a div, not a main, for a preview inside another page.
boolean · default false
- className
Extra classes on the root, which holds the container query.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| navbar | NavbarProps | - | The Navbar's props. It floats as a pill once the page scrolls, and folds into a menu below 42rem. |
| header | HeadlineProps | - | The headline at the top of the page. It renders as the page's one h1. |
| story | { id?: string; headline: HeadlineProps; paragraphs: ReactNode[] } | - | Why you started, as a headline beside a few short paragraphs. They stack below 64rem. Its id defaults to "story". |
| stats | { id?: string; headline?: HeadlineProps; label?: string; items: StatBandItem[] } | - | Two to four figures, as a Stat Band, each with a `value`, a `label`, and an optional `note`. Its id defaults to "numbers". With no headline, `label` names it for screen readers, default "Numbers". With no items, it is left out. |
| values | { id?: string; headline?: HeadlineProps; label?: string; items: FeatureGridItemProps[] } | - | What you hold to, one Feature Grid cell each. Its id defaults to "values". With no headline, `label` names it for screen readers, default "Values". With no items, it is left out. |
| team | { id?: string; headline?: HeadlineProps; label?: string; team: TeamGridProps } | - | The people, as a Team Grid. Its id defaults to "team". With no headline, `label` names it for screen readers, default "Team". With no people and no open roles, it is left out. |
| cta | CallToActionBandProps | - | The closing band above the footer. |
| footer | FooterProps | - | The Footer's props. |
| renderLink | (props: NavbarLinkProps) => ReactNode | a plain <a> | Draws every link in the navbar, the team, the closing band, and the footer, unless a part sets its own. |
| embedded | boolean | false | Renders the content in a div, not a main, for a preview inside another page. |
| className | string | - | Extra classes on the root, which holds the container query. |
Accessibility
- The content sits in a main landmark, between the navbar's nav and the footer, so skip links and screen readers find it.
- The header renders the page's one h1. Each section's headline is an h2, and each person's name an h3.
- Each section has an id, so the navbar can link to it, and it scrolls into view below the floating navbar.
- Each part keeps its own accessibility, listed on its page.
Examples
A short about page
<AboutPage
navbar={{ brand: "Workspace", links: [{ href: "/about", label: "About", current: true }] }}
header={{ title: "We build the app", muted: "we wanted for our own teams." }}
story={{
headline: { title: "Why we", muted: "started." },
paragraphs: ["We ran teams with a spreadsheet and a chat.", "So we built one place for the week."],
}}
team={{
headline: { title: "The people." },
team: { people: [{ name: "Ana Lindqvist", role: "Founder" }] },
}}
cta={{ title: "Plan your week.", action: { label: "Start free", href: "/signup" } }}
footer={{
brand: "Workspace",
columns: [{ title: "Company", links: [{ href: "/about", label: "About" }] }],
legal: "© 2026 Workspace",
}}
/>