Customizing look and feel
Match the tax documentation form to your product's design.
The SDK ships a default stylesheet so your first render isn't broken, but it's a starting point rather than a design system you're expected to keep. Every element carries a namespaced taxbit- class, and those class names are a stable contract — restyle what you need or replace the styling outright. This page covers what you can change, the class names to target, and how far you can take it.
For the rest of the integration flow, see the Questionnaire integration guide.
Style it with your AI coding agent
Taxbit publishes an agent skill that teaches Claude Code, Cursor, GitHub Copilot, Windsurf, and 40+ other agents how the form is built — every class name, the override patterns, and the gotchas. It's the fastest path from the default styling to your brand.
npx skills add taxbit-shared/taxbit-skillsThen prompt your agent inside your own codebase:
Style the Taxbit tax documentation form to match our design system. Our design
tokens are in <path/to/tokens> and our components are in <path/to/components>.
Write the overrides to src/styles/taxbit-overrides.css and import it after the
Taxbit base stylesheet. Match our colors, typography, border radius, spacing,
and input and button styles.Point the agent at your existing tokens and components. That's what makes the output match your product rather than approximate it. The rest of this page documents what the agent works from, so you can review and extend its work.
Claude Code users can install it as a plugin instead, which also adds the /taxbit:react-sdk command:
/plugin marketplace add https://github.com/taxbit-shared/taxbit-skills.gitWhat you can change
| You control | Taxbit controls |
|---|---|
| Colors, fonts, and text sizes | Which questions appear |
| Spacing, borders, and radius | The order they appear in |
| Button and input styles | How each question is worded |
| Form width | Which answers are valid |
| Whether labels sit above or beside inputs | Which form type applies (W-9, W-8BEN-E, MRDP, CRS, CARF) |
Presentation is yours. Question content and sequence are driven by tax regulation and the form type, so they aren't styling options — changing them would put your filings at risk. In practice you can make the form look native to your product, but you can't reorder or reword the questions.
How styling works
TaxbitQuestionnaire and TaxbitCuringDocumentation render the same class names, so one stylesheet covers both.
- It's ordinary CSS. Write rules against the
taxbit-class names the same way you style any other part of your app. - Every class is namespaced. The
taxbit-prefix keeps SDK styles from colliding with yours. - Your brand values live wherever you want them. Declare your own CSS variables at the top of your stylesheet and reuse them throughout — see Set your brand basics.
- Import your stylesheet after the SDK's. Whichever comes last wins.
import '@taxbit/react-sdk/style/inline.css'; // optional starting point — at most one
import './taxbit-overrides.css'; // yours, always imported afterStyling escalates through five stages, and each is a reasonable place to stop. Most integrations stop after the second.
Choose a starting point
Import at most one base stylesheet. Importing none is a normal choice.
| Import | Label position | What you get |
|---|---|---|
| (none) | your choice | No SDK styling. You write every rule. Cleanest slate, most work. |
style/minimal.css | your choice | Structure only — form width, field spacing, and the four status-color tokens. Best starting point if you intend to restyle. |
style/inline.css | beside the input | The default look — layout, inputs, buttons, badges. Use when the default is close and you only need to tweak. |
style/basic.css | above the input | Same as inline.css, with each label on its own line above the field. |
The meaningful difference between inline.css and basic.css is label placement. .taxbit-row uses flex-direction: row in one and column in the other. Pick whichever matches the forms elsewhere in your product. They also differ slightly in radio-option spacing and one postal-code margin.
style/index.css is a demo-app stylesheet. It sets fonts, text color, and padding on :root and html, so it restyles your entire page rather than the form. Don't import it in production.
Theme with color tokens
From 6.0.0, the bundled stylesheets declare their colors as custom properties on :root. With inline.css or basic.css, the quickest way to recolor the form is to override these tokens once. Every control that uses them follows, and you don't have to match any selector specificity.
/* taxbit-overrides.css — imported after the base stylesheet */
:root {
--taxbit-color-primary: #6b21a8;
--taxbit-color-primary-hover: #581c87;
--taxbit-color-on-primary: #ffffff;
--taxbit-color-text: #1f2937;
--taxbit-color-border: #6b7280;
--taxbit-color-error: #b91c1c;
--taxbit-color-success: #15803d;
--taxbit-color-warning: #b45309;
--taxbit-color-info: #1d4ed8;
}| Token | Default | Colors |
|---|---|---|
--taxbit-color-primary | #0060df | Next and Submit background |
--taxbit-color-primary-hover | #0250bb | Next and Submit on hover |
--taxbit-color-primary-disabled | #aab3bb | Next and Submit while unavailable |
--taxbit-color-on-primary | #ffffff | Text on the primary button |
--taxbit-color-secondary-bg | #f9f9f9 | Back and other secondary buttons |
--taxbit-color-secondary-bg-hover | #e9e9e9 | Secondary buttons on hover |
--taxbit-color-secondary-text | #1a1a1a | Secondary button text |
--taxbit-color-secondary-text-hover | #000000 | Secondary button text on hover |
--taxbit-color-text | #12263f | Body text and headings |
--taxbit-color-text-muted | #5c6773 | Sub-labels, sub-titles, progress text, language picker |
--taxbit-color-placeholder | #767676 | Placeholder text |
--taxbit-color-border | #7d899e | Input and select borders |
--taxbit-color-divider | #e4ebf6 | Header, section and footer dividers |
--taxbit-color-error | #b22222 | Error messages, invalid badges, the error state |
--taxbit-color-warning | #a06000 | Warning messages and pending badges |
--taxbit-color-success | #008000 | Success messages and valid badges |
--taxbit-color-info | #0000ff | Informational messages |
minimal.css styles no button or text colors, so it declares only the four status tokens: --taxbit-color-error, -warning, -success and -info.
Re-check contrast when you change the tokens. The defaults meet WCAG 2.1 AA on a white background. --taxbit-color-text-muted, --taxbit-color-placeholder, --taxbit-color-border and the status colors are the values most likely to fall below AA when you swap in brand colors.
Tokens and class rules work together. Use tokens for the palette, and class rules (below) for everything else: fonts, radius, spacing and sizing.
Set your brand basics
These rules cover most of what makes the form read as someone else's. Declare your own CSS variables at the top of the file so brand values live in one place:
/* taxbit-overrides.css — imported after the base stylesheet */
.taxbit-page {
--brand-primary: #6b21a8;
--brand-primary-hover: #581c87;
--brand-text: #1f2937;
--brand-border: #d1d5db;
--brand-radius: 6px;
--brand-font: 'Inter var', system-ui, sans-serif;
font-family: var(--brand-font);
color: var(--brand-text);
max-width: 820px;
}
/* Primary action — Next and Submit */
.taxbit-step-actions .taxbit-primary-actions > button {
background-color: var(--brand-primary);
border-color: var(--brand-primary);
color: #fff;
}
.taxbit-step-actions .taxbit-primary-actions > button:hover {
background-color: var(--brand-primary-hover);
border-color: var(--brand-primary-hover);
}
/* Secondary action — Back and Cancel */
.taxbit-step-actions .taxbit-secondary-actions > button {
background-color: transparent;
border-color: var(--brand-border);
color: var(--brand-text);
}
button.taxbit-button {
border-radius: var(--brand-radius);
font-family: var(--brand-font);
}
.taxbit-input,
.taxbit-select {
border: 1px solid var(--brand-border);
border-radius: var(--brand-radius);
color: var(--brand-text);
font-family: var(--brand-font);
height: auto;
padding: 8px 10px;
}
.taxbit-page-title { color: var(--brand-text); font-size: 1.4em; }
.taxbit-label { font-weight: 500; }
/* Status text */
.taxbit-error-message,
.taxbit-badge.invalid { color: #b91c1c; }
.taxbit-success-message,
.taxbit-badge.valid { color: #15803d; }
.taxbit-warning-message,
.taxbit-badge.pending { color: #b45309; }
.taxbit-info-message { color: #1d4ed8; }Replace the status colors first. The defaults (#b22222, #008000, #a06000, #0000ff) are deliberately generic and rarely sit well next to a brand palette. With inline.css or basic.css, overriding the four status tokens is the shortest route. The class rules above work too, and they're the only route if you import no base stylesheet.
Target individual elements
Every class below is a stable override target. Some carry no default styling at all — they're structural wrappers exposed so you have something to hook onto, and they're just as valid to target.
Page, header, and footer
| Class | Element |
|---|---|
.taxbit-page | The whole form — controls width and padding |
.taxbit-page-header | Header bar |
.taxbit-page-title | Step heading |
.taxbit-page-sub-title | Subtitle under the heading |
.taxbit-progress-status | The "Step 2 of 5" text |
.taxbit-select-language | Language picker in the header |
.taxbit-page-main / .taxbit-page-content | Body of the form |
.taxbit-page-footer / .taxbit-footer | Footer region |
.taxbit-page-actions / .taxbit-step-actions | Action bar holding the buttons |
.taxbit-primary-actions | Next and Submit — first in the DOM; shown on the right on wide screens |
.taxbit-secondary-actions | Back and Cancel — after the primary actions in the DOM; shown on the left on wide screens |
Groups of questions
| Class | Element |
|---|---|
.taxbit-section | A titled group of related questions |
.taxbit-section-header | Group header |
.taxbit-section-header-title | Group title (an <h4>) |
.taxbit-section-header-sub-title | Group subtitle |
.taxbit-section-header-action | Action slot at the right of the group header |
.taxbit-section-content | Questions inside the group |
.taxbit-form-text | Standalone paragraph of explanation |
A single question
| Class | Element |
|---|---|
.taxbit-row | One question — label plus input. Gets .taxbit-error when invalid |
.taxbit-label | Question text — a <label> for a single control, a <div> for a group (radio set, date, phone, address) |
.taxbit-required-marker | Required indicator beside the label — a decorative <span aria-hidden="true">, unstyled by default |
.taxbit-sub-label | Helper text under the question |
.taxbit-row-content / .taxbit-row-value / .taxbit-input-group | Wrappers around the input |
.taxbit-row-actions | Action column at the right of a question |
.taxbit-row-action-button | Small button in that column |
.taxbit-row-edit-button | Edit button |
.taxbit-show-button / .taxbit-hide-button | Show and hide toggles on masked fields such as TIN |
.taxbit-input-status / .taxbit-input-status-footer | Validation status shown under an input |
Inputs
| Class | Element |
|---|---|
.taxbit-input | Text input |
.taxbit-textarea | Multi-line input |
.taxbit-input-file | File upload |
.taxbit-password | Masked field |
.taxbit-select | Dropdown |
.taxbit-country-code-select | Country dropdown, width-constrained |
.taxbit-select-day / -month / -year | The three parts of a date input |
.taxbit-checkbox / .taxbit-checkbox-label | Checkbox and its label; .disabled for the greyed-out state |
.taxbit-radio-buttons | Radio group |
.taxbit-radio-button | One option |
.taxbit-radio-button-option | That option's label |
.taxbit-radio-button-sub-option | Helper text under an option |
.taxbit-placeholder | Placeholder text |
.taxbit-file-selected / .taxbit-filename | Selected-file display |
Address fields
| Class | Element |
|---|---|
.taxbit-address | Address block |
.taxbit-address-line-1 / -line-2 / -region / -country | Individual lines |
.taxbit-city / .taxbit-state / .taxbit-postal-code | City, state, and postal code |
Buttons and status messages
| Class | Element |
|---|---|
button.taxbit-button | Any button |
.taxbit-button-disabled | Unavailable button. Pair it with [aria-disabled='true'] (see Lay out the step actions) |
.taxbit-badge | Validation badge; .valid, .invalid, and .pending modifiers. Used for TIN and GIIN results and, from 6.2.0, the "Invalid" VAT badge |
.taxbit-error-message | Error text |
.taxbit-success-message | Success text |
.taxbit-warning-message | Warning text |
.taxbit-info-message | Informational text |
.taxbit-spinner-icon | Loading spinner |
.taxbit-error | Applied to a question when its answer is invalid |
.taxbit-disabled | Greyed-out state |
Lay out the step actions
From 6.0.0, the primary action (Next, Submit) comes before Back in the DOM. That way screen readers and the Tab key reach it first, and a stacked layout shows it on top. The bundled stylesheets handle the layout for you:
- Wide screens —
.taxbit-step-actionsusesflex-direction: row-reverse, so Back sits on the left and Next on the right. - Below 600px — the buttons stack in DOM order, with the primary action on top.
If you style .taxbit-step-actions yourself (or import no base stylesheet), a plain row shows Next on the left. To keep Back on the left, reverse the row:
.taxbit-step-actions { display: flex; flex-direction: row-reverse; }Style unavailable buttons by attribute, not :disabled. While Next or Submit is working, or after a submit, it carries aria-disabled="true" and the taxbit-button-disabled class rather than the disabled attribute, so focus stays on the button. A rule that targets only :disabled won't match. Include all three:
button.taxbit-button:disabled,
button.taxbit-button.taxbit-button-disabled,
button.taxbit-button[aria-disabled='true'] {
opacity: 0.6;
cursor: default;
}Style the required marker
The required indicator is a separate <span class="taxbit-required-marker" aria-hidden="true"> beside the label, not an asterisk inside the label text (6.0.0 and later). The bundled stylesheets leave it unstyled, so it looks as it did before. Target the span to change its color or spacing:
.taxbit-required-marker { color: var(--taxbit-color-error); margin-left: 2px; }Target one form or one question
Three class families are generated at runtime from the form type or the question key, lowercased with dashes between words. Derive the name from the key rather than copying a fixed list — the list grows.
| Pattern | Example | Use it to |
|---|---|---|
.taxbit-form-<form-name> | .taxbit-form-w-8ben-e | Style one form type differently |
.taxbit-question-<field-name> | .taxbit-question-tin | Style a single question |
.taxbit-error-message-<field-name> | .taxbit-error-message-tin | Style one question's error message |
/* Monospace the TIN field so digits line up */
.taxbit-question-tin .taxbit-input {
font-family: ui-monospace, monospace;
letter-spacing: 0.05em;
}
/* Widen only W-8BEN-E, which has the most questions */
.taxbit-form-w-8ben-e .taxbit-page { max-width: 960px; }Some taxbit-error-* names are element ids, not classes. Values like taxbit-error-dob are referenced by aria-describedby for screen readers and won't respond to CSS overrides. Use the .taxbit-error-message-<field-name> class for per-question error styling.
Replace the styling entirely
Import no base stylesheet. You write every rule, there's no specificity to work around, and nothing the SDK ships can shift under you on upgrade. The class reference above is the complete surface area.
import { TaxbitQuestionnaire } from '@taxbit/react-sdk';
import './taxbit-theme.css'; // entirely yoursThis is usually right when you have an established design system, use Tailwind or a component library, or run strict design review. More work up front, less over time.
Resolve overrides that don't apply
When two rules target the same element, the more specific one wins. A few SDK rules are more specific than you'd expect: buttons are styled as button.taxbit-button rather than .taxbit-button, and the Next and Submit buttons through .taxbit-step-actions .taxbit-primary-actions > button.
/* Loses to the base stylesheet */
.taxbit-button { background: #6b21a8; }
/* Wins — qualified with the element, as the SDK rule is */
button.taxbit-button { background: #6b21a8; }
/* Wins — matches the SDK's longer path */
.taxbit-step-actions .taxbit-primary-actions > button { background: #6b21a8; }Match the shape of the SDK rule rather than reaching for !important. If you import no base stylesheet, none of this applies — your rules are the only rules. For colors, overriding a token avoids the specificity question entirely.
Default values
Relevant when you're extending the shipped look rather than replacing it. All colors below are --taxbit-color-* tokens from 6.0.0 (see Theme with color tokens). Several were raised in 6.0.0 to meet AA contrast.
| Element | Default |
|---|---|
| Primary button | #0060df; hover #0250bb; disabled #aab3bb; text #ffffff |
| Secondary button | #f9f9f9; hover #e9e9e9; text #1a1a1a; hover text #000000 |
| Body text and headings | #12263f; muted text #5c6773; placeholder #767676 |
| Borders and dividers | dividers #e4ebf6; input and select borders #7d899e |
| Status colors | success #008000, error #b22222, warning and pending #a06000, info #0000ff |
| Form width | max-width: 600px |
| Input and select height | 33px |
| Border radius | 4px inputs, 8px buttons |
| Font stack | Inter, system-ui, Avenir, Helvetica, Arial, sans-serif |
Best practices
- Start from
minimal.csswhen you intend to restyle. It gives you structure and status colors without a full visual opinion to unwind, so you spend your time writing rules rather than cancelling them. - Theme colors through the tokens. With
inline.cssorbasic.css, setting--taxbit-color-*on:rootrecolors every control without specificity fights. - Re-check contrast after theming. The shipped palette meets WCAG 2.1 AA. Muted text, placeholders, borders and status colors are the first to fall short with a brand palette.
- Declare your brand values once at the top of your override file. Your own CSS variables on
.taxbit-pagekeep colors, fonts, and radius in one place and cascade to every rule below. - Keep all overrides in one stylesheet, imported after the SDK's. One file makes the full extent of your customization reviewable and keeps import order easy to reason about.
- Target class names directly. Flat, class-based selectors survive internal DOM changes; selectors that walk the form's structure don't.
- Replace the default status colors. The defaults are deliberately generic and are the fastest visual tell that a form isn't yours.
- Style
TaxbitCuringDocumentationandTaxbitQuestionnairetogether. They render the same classes, so one stylesheet covers both — verify the curing flow after restyling rather than treating it separately. - Re-check your overrides when you upgrade. DOM and class changes are listed in the Upgrade guide. If you've styled deeply, read it before bumping the version.
Pitfalls
- Importing your stylesheet before the SDK's. Later rules win. If your CSS appears to do nothing at all, check import order before anything else.
- Importing
style/index.cssin production. It styles:rootandhtml, so fonts, text color, and padding change across your entire page rather than inside the form. - Writing bare
.taxbit-buttonrules. The SDK qualifies its button selectors with the element name, so an unqualified class selector silently loses. - Styling
taxbit-error-*ids. Several of those names are element ids used foraria-describedby, not classes. Per-question error styling uses.taxbit-error-message-<field-name>. - Depending on the form's internal structure. Long descendant chains,
:nth-child, and sibling selectors break when the form's internals change. Class names are the contract; nesting depth and tag names are not. - Setting input height without clearing the default.
.taxbit-inputand.taxbit-selectship atheight: 33px. Setheight: autoand use padding instead of fighting the fixed value. - Laying out the step actions as a plain row. From
6.0.0, Next comes before Back in the DOM. Withoutflex-direction: row-reverse, a custom row shows Next on the left. - Muting unavailable buttons with
:disabledonly. The primary action usesaria-disabled="true"and.taxbit-button-disabled, not thedisabledattribute. - Styling labels with
label.taxbit-label. Group labels (radio sets, dates, phone numbers, addresses) are<div class="taxbit-label">. Use the class alone. - Re-adding an asterisk with
::afteron the label. The required marker is already a separate span. Style.taxbit-required-markerinstead, or you'll show two. - Hard-coding the list of generated class names.
.taxbit-question-*and.taxbit-form-*names are derived from keys and grow with new fields and form types. Build them from the key.
Next steps
- Questionnaire integration guide — the full setup, auth, and submission flow
- Component and hook reference — every prop, hook return value, and type
- Read more about Taxbit agent skills — more information on the React SDK skill
Updated 9 days ago
