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:
| Variable | Default | Description |
|---|---|---|
--op-font-family | system-ui, sans-serif | Font family for the entire policy |
--op-font-size-body | 1rem | Body text size |
--op-font-size-heading | 1.125rem | Section heading size |
--op-font-weight-heading | 600 | Section heading weight |
--op-line-height | 1.7 | Line height for body text |
--op-body-color | #374151 | Body text color |
--op-heading-color | #111827 | Heading and bold text color |
--op-link-color | #2563eb | Link color |
--op-link-color-hover | #1d4ed8 | Link hover color |
--op-section-gap | 2rem | Vertical gap between sections |
--op-border-color | #e5e7eb | Section divider color |
--op-border-radius | 0.375rem | Border 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:
| Attribute | Element | Description |
|---|---|---|
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.