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, status colors. 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.
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 are the plain CSS keywords firebrick, green, goldenrod, and blue, which rarely sit well next to a brand palette.
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 | Right side — Next and Submit |
.taxbit-secondary-actions | Left side — Back and Cancel |
Groups of questions
| Class | Element |
|---|---|
.taxbit-section | A titled group of related questions |
.taxbit-section-header | Group header |
.taxbit-section-header-title | Group title |
.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 |
.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 | Disabled button |
.taxbit-badge | Validation badge; .valid, .invalid, and .pending modifiers |
.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 |
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.
Default values
Relevant when you're extending the shipped look rather than replacing it.
| Element | Default |
|---|---|
| Primary button | rgb(0, 96, 223); hover rgb(2, 80, 187); disabled rgb(170, 179, 187); text #ffffff |
| Secondary button | #f9f9f9; hover #e9e9e9; text #1a1a1a |
| Body text and headings | #12263f |
| Borders and dividers | #e4ebf6; input borders #bfcde2 |
| Status colors | success green, error firebrick, warning and pending goldenrod, info blue |
| 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. - 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.
firebrick,green,goldenrod, andblueare 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 recorded in the SDK changelog. 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. - 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 about 4 hours ago

