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-skills

Then 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.git

What you can change

You controlTaxbit controls
Colors, fonts, and text sizesWhich questions appear
Spacing, borders, and radiusThe order they appear in
Button and input stylesHow each question is worded
Form widthWhich answers are valid
Whether labels sit above or beside inputsWhich 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 after

Styling 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.

ImportLabel positionWhat you get
(none)your choiceNo SDK styling. You write every rule. Cleanest slate, most work.
style/minimal.cssyour choiceStructure only — form width, field spacing, and the four status-color tokens. Best starting point if you intend to restyle.
style/inline.cssbeside the inputThe default look — layout, inputs, buttons, badges. Use when the default is close and you only need to tweak.
style/basic.cssabove the inputSame 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;
}
TokenDefaultColors
--taxbit-color-primary#0060dfNext and Submit background
--taxbit-color-primary-hover#0250bbNext and Submit on hover
--taxbit-color-primary-disabled#aab3bbNext and Submit while unavailable
--taxbit-color-on-primary#ffffffText on the primary button
--taxbit-color-secondary-bg#f9f9f9Back and other secondary buttons
--taxbit-color-secondary-bg-hover#e9e9e9Secondary buttons on hover
--taxbit-color-secondary-text#1a1a1aSecondary button text
--taxbit-color-secondary-text-hover#000000Secondary button text on hover
--taxbit-color-text#12263fBody text and headings
--taxbit-color-text-muted#5c6773Sub-labels, sub-titles, progress text, language picker
--taxbit-color-placeholder#767676Placeholder text
--taxbit-color-border#7d899eInput and select borders
--taxbit-color-divider#e4ebf6Header, section and footer dividers
--taxbit-color-error#b22222Error messages, invalid badges, the error state
--taxbit-color-warning#a06000Warning messages and pending badges
--taxbit-color-success#008000Success messages and valid badges
--taxbit-color-info#0000ffInformational 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

ClassElement
.taxbit-pageThe whole form — controls width and padding
.taxbit-page-headerHeader bar
.taxbit-page-titleStep heading
.taxbit-page-sub-titleSubtitle under the heading
.taxbit-progress-statusThe "Step 2 of 5" text
.taxbit-select-languageLanguage picker in the header
.taxbit-page-main / .taxbit-page-contentBody of the form
.taxbit-page-footer / .taxbit-footerFooter region
.taxbit-page-actions / .taxbit-step-actionsAction bar holding the buttons
.taxbit-primary-actionsNext and Submit — first in the DOM; shown on the right on wide screens
.taxbit-secondary-actionsBack and Cancel — after the primary actions in the DOM; shown on the left on wide screens

Groups of questions

ClassElement
.taxbit-sectionA titled group of related questions
.taxbit-section-headerGroup header
.taxbit-section-header-titleGroup title (an <h4>)
.taxbit-section-header-sub-titleGroup subtitle
.taxbit-section-header-actionAction slot at the right of the group header
.taxbit-section-contentQuestions inside the group
.taxbit-form-textStandalone paragraph of explanation

A single question

ClassElement
.taxbit-rowOne question — label plus input. Gets .taxbit-error when invalid
.taxbit-labelQuestion text — a <label> for a single control, a <div> for a group (radio set, date, phone, address)
.taxbit-required-markerRequired indicator beside the label — a decorative <span aria-hidden="true">, unstyled by default
.taxbit-sub-labelHelper text under the question
.taxbit-row-content / .taxbit-row-value / .taxbit-input-groupWrappers around the input
.taxbit-row-actionsAction column at the right of a question
.taxbit-row-action-buttonSmall button in that column
.taxbit-row-edit-buttonEdit button
.taxbit-show-button / .taxbit-hide-buttonShow and hide toggles on masked fields such as TIN
.taxbit-input-status / .taxbit-input-status-footerValidation status shown under an input

Inputs

ClassElement
.taxbit-inputText input
.taxbit-textareaMulti-line input
.taxbit-input-fileFile upload
.taxbit-passwordMasked field
.taxbit-selectDropdown
.taxbit-country-code-selectCountry dropdown, width-constrained
.taxbit-select-day / -month / -yearThe three parts of a date input
.taxbit-checkbox / .taxbit-checkbox-labelCheckbox and its label; .disabled for the greyed-out state
.taxbit-radio-buttonsRadio group
.taxbit-radio-buttonOne option
.taxbit-radio-button-optionThat option's label
.taxbit-radio-button-sub-optionHelper text under an option
.taxbit-placeholderPlaceholder text
.taxbit-file-selected / .taxbit-filenameSelected-file display

Address fields

ClassElement
.taxbit-addressAddress block
.taxbit-address-line-1 / -line-2 / -region / -countryIndividual lines
.taxbit-city / .taxbit-state / .taxbit-postal-codeCity, state, and postal code

Buttons and status messages

ClassElement
button.taxbit-buttonAny button
.taxbit-button-disabledUnavailable button. Pair it with [aria-disabled='true'] (see Lay out the step actions)
.taxbit-badgeValidation badge; .valid, .invalid, and .pending modifiers. Used for TIN and GIIN results and, from 6.2.0, the "Invalid" VAT badge
.taxbit-error-messageError text
.taxbit-success-messageSuccess text
.taxbit-warning-messageWarning text
.taxbit-info-messageInformational text
.taxbit-spinner-iconLoading spinner
.taxbit-errorApplied to a question when its answer is invalid
.taxbit-disabledGreyed-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-actions uses flex-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.

PatternExampleUse it to
.taxbit-form-<form-name>.taxbit-form-w-8ben-eStyle one form type differently
.taxbit-question-<field-name>.taxbit-question-tinStyle a single question
.taxbit-error-message-<field-name>.taxbit-error-message-tinStyle 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 yours

This 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.

ElementDefault
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 dividersdividers #e4ebf6; input and select borders #7d899e
Status colorssuccess #008000, error #b22222, warning and pending #a06000, info #0000ff
Form widthmax-width: 600px
Input and select height33px
Border radius4px inputs, 8px buttons
Font stackInter, system-ui, Avenir, Helvetica, Arial, sans-serif

Best practices

  • Start from minimal.css when 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.css or basic.css, setting --taxbit-color-* on :root recolors 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-page keep 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 TaxbitCuringDocumentation and TaxbitQuestionnaire together. 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.css in production. It styles :root and html, so fonts, text color, and padding change across your entire page rather than inside the form.
  • Writing bare .taxbit-button rules. 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 for aria-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-input and .taxbit-select ship at height: 33px. Set height: auto and 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. Without flex-direction: row-reverse, a custom row shows Next on the left.
  • Muting unavailable buttons with :disabled only. The primary action uses aria-disabled="true" and .taxbit-button-disabled, not the disabled attribute.
  • 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 ::after on the label. The required marker is already a separate span. Style .taxbit-required-marker instead, 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