OpenPolicy
React

Styling

Style policy components with CSS, Tailwind, or custom React components

There are three ways to style policy components, from simple overrides to fully custom rendering.

Method 1: CSS variables

When you use the <OpenPolicy> provider, default styles are injected automatically. You can override any aspect by setting CSS custom properties on the .op-policy class:

.op-policy {
  --op-font-family: "Inter", sans-serif;
  --op-font-size-body: 0.9375rem;
  --op-body-color: #1f2937;
  --op-heading-color: #111827;
  --op-link-color: #7c3aed;
  --op-link-color-hover: #6d28d9;
  --op-section-gap: 2.5rem;
}

All available CSS variables:

VariableDefaultDescription
--op-font-familysystem-ui, sans-serifFont family for the entire policy
--op-font-size-body1remBody text size
--op-font-size-heading1.125remSection heading size
--op-font-weight-heading600Section heading weight
--op-line-height1.7Line height for body text
--op-body-color#374151Body text color
--op-heading-color#111827Heading and bold text color
--op-link-color#2563ebLink color
--op-link-color-hover#1d4ed8Link hover color
--op-section-gap2remVertical gap between sections
--op-border-color#e5e7ebSection divider color
--op-border-radius0.375remBorder radius (for future use)

Method 2: Tailwind

Policy components render semantic HTML with data-op-* attributes on every element. You can target these with Tailwind's arbitrary variant syntax (Tailwind v4):

import { PrivacyPolicy } from "@openpolicy/react";

const policyStyles = [
  "**:data-op-section:mb-10 **:data-op-section:border-b **:data-op-section:border-gray-200 **:data-op-section:pb-10",
  "**:data-op-heading:text-xl **:data-op-heading:font-semibold **:data-op-heading:mb-4",
  "**:data-op-paragraph:text-sm **:data-op-paragraph:text-gray-500 **:data-op-paragraph:leading-relaxed **:data-op-paragraph:mb-3",
].join(" ");

export function PrivacyPolicyPage() {
  return (
    <div className={policyStyles}>
      <PrivacyPolicy />
    </div>
  );
}

Available data-op-* attributes:

AttributeElementDescription
data-op-policy<div>Root wrapper for the entire policy
data-op-section<section>Each policy section
data-op-heading<h3>Section heading
data-op-paragraph<p>Paragraph
data-op-list<ul>Unordered list
data-op-list-item<li>List item
data-op-bold<strong>Bold/emphasized text
data-op-link<a>Hyperlink

When using Tailwind, you may want to disable the default injected styles. Pass style={{ all: "unset" }} to the component or avoid using the <OpenPolicy> provider (and add styles manually instead).

Method 3: Custom components

Pass a components prop to replace individual rendering elements with your own React components. This gives you full control over markup and styling.

import { OpenPolicy, PrivacyPolicy } from "@openpolicy/react";
import openpolicy from "./openpolicy";

export function PrivacyPolicyPage() {
  return (
    <OpenPolicy config={openpolicy}>
      <PrivacyPolicy
        components={{
          Heading: ({ node }) => (
            <h2 className="text-2xl font-bold">{node.value}</h2>
          ),
          Paragraph: ({ children }) => (
            <p className="text-sm text-gray-500">{children}</p>
          ),
        }}
      />
    </OpenPolicy>
  );
}

The PolicyComponents interface:

interface PolicyComponents {
  Section?: ComponentType<{ section: DocumentSection; children: ReactNode }>;
  Heading?: ComponentType<{ node: HeadingNode }>;
  Paragraph?: ComponentType<{ node: ParagraphNode; children: ReactNode }>;
  List?: ComponentType<{ node: ListNode; children: ReactNode }>;
  Text?: ComponentType<{ node: TextNode }>;
  Bold?: ComponentType<{ node: BoldNode }>;
  Link?: ComponentType<{ node: LinkNode }>;
}

You only need to override the components you want to customize — the rest fall back to the defaults.

On this page