A web part that ignores the site theme sticks out like a sore thumb — bright blue buttons on a dark-themed site, hard-coded colours that vanish on a coloured section background. Modern SharePoint pages let users apply themes and section backgrounds, and a well-built web part adapts to all of them. The tools are IReadonlyTheme for the colours and Fluent UI React for the components.

Why hard-coded colours fail

Modern pages support tenant and site themes, plus per-section backgrounds (including dark "emphasis" sections). If you bake #0078d4 into your CSS, your web part looks wrong the moment someone changes the theme or drops it into a dark section. The fix is to read colours from the theme at runtime and re-render when it changes.

Receiving the theme

SPFx delivers the current theme to your web part through the ThemeProvider. Acquire it in onInit(), store the theme, and subscribe to changes:

import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme }
    from '@microsoft/sp-component-base';

private _themeProvider: ThemeProvider;
private _theme: IReadonlyTheme | undefined;

protected onInit(): Promise<void> {
  this._themeProvider =
    this.context.serviceScope.consume(ThemeProvider.serviceKey);
  this._theme = this._themeProvider.tryGetTheme();
  this._themeProvider.themeChangedEvent.add(this, this._onThemeChanged);
  return super.onInit();
}

private _onThemeChanged(args: ThemeChangedEventArgs): void {
  this._theme = args.theme;
  this.render();   // re-render with new colours
}

Using semantic colours

Don't read raw palette entries — read semantic slots, which are defined for every theme including dark ones. bodyText, bodyBackground, link, and buttonBackground always resolve to a sensible value for the current theme:

public render(): void {
  const semantic = this._theme && this._theme.semanticColors;
  const text = semantic?.bodyText ?? '#323130';
  const bg   = semantic?.bodyBackground ?? '#ffffff';
  const link = semantic?.link ?? '#0078d4';

  this.domElement.innerHTML = `
    <div style="color:${text};background:${bg};padding:16px">
      <h2>Contracts</h2>
      <a style="color:${link}" href="#">View all</a>
    </div>`;
}
Prefer semantic colours over palette colours. palette.themePrimary tells you the accent; semanticColors.bodyText tells you "the right text colour for this surface." Semantic slots are what keep your web part legible on a dark emphasis section automatically.

Fluent UI: components that already theme themselves

The bigger win is to stop styling primitives by hand and use Fluent UI React (the office-ui-fabric-react / @fluentui/react package that ships with SPFx). Its components read the ambient theme, are keyboard-accessible, and match the rest of SharePoint out of the box:

import * as React from 'react';
import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { DetailsList, IColumn } from 'office-ui-fabric-react/lib/DetailsList';

export const ContractForm: React.FC = () => (
  <div>
    <TextField label="Contract title" required />
    <PrimaryButton text="Save" />
    <DefaultButton text="Cancel" />
  </div>
);

A PrimaryButton picks up the site's accent colour; a DetailsList renders a sortable, accessible, virtualized grid that looks identical to native SharePoint lists. You get consistency and accessibility for free, instead of reinventing — and likely under-delivering — both.

Accessibility isn't optional

If your organisation has accessibility obligations (and most do), Fluent UI is the lowest-effort way to meet them. A custom-styled <div onClick> "button" fails keyboard and screen-reader testing; a Fluent PrimaryButton passes it without you writing a line of ARIA.

Putting it together

The recipe for a web part that feels native:

  1. Read the theme via ThemeProvider and subscribe to themeChangedEvent.
  2. Pass the theme (or just the semantic colours) into your React component as a prop.
  3. Build the UI from Fluent UI components and let them consume the theme.
  4. Use semantic colours for any custom surfaces you style yourself.

The result is a web part that looks intentional on a default site, a branded site, and a dark emphasis section alike — and that passes accessibility review. Respecting the theme is a small amount of code that separates a professional solution from an obvious bolt-on.

DS

Dhaval Shah

Cloud Solution Architect — 18+ years across SharePoint, Microsoft 365, and Azure AI.