UNPKG

@tantainnovative/ndpr-toolkit

Version:

Nigeria Data Protection Toolkit — enterprise-grade compliance components for the Nigeria Data Protection Act (NDPA) 2023

1,481 lines (1,268 loc) 40.2 kB
/* ========================================================================== * @tantainnovative/ndpr-toolkit — default component styles * * One stylesheet, no Tailwind dependency. Driven by --ndpr-* CSS custom * properties so consumers can theme by overriding tokens at any level * (`:root`, `[data-theme="dark"]`, or scoped to a wrapper). * * Import once in your app root: * * // app/layout.tsx (Next.js) or src/main.tsx (Vite/CRA) * import "@tantainnovative/ndpr-toolkit/styles"; * * Naming convention (BEM-ish, prefixed with `ndpr-`): * .ndpr-{component} — component root * .ndpr-{component}__{element} — child part * .ndpr-{component}--{modifier} — variant or state * * If you want to opt out completely (build your own design system), import * `@tantainnovative/ndpr-toolkit/unstyled` instead — components render with * no class names so your CSS rules apply unfiltered. * ========================================================================== */ /* ── Design tokens ─────────────────────────────────────────────────────── */ :root { /* Colors — light theme */ --ndpr-primary: 37 99 235; /* blue-600 RGB */ --ndpr-primary-hover: 29 78 216; /* blue-700 RGB */ --ndpr-primary-foreground: 255 255 255; --ndpr-background: 255 255 255; --ndpr-surface: 249 250 251; /* gray-50 RGB */ --ndpr-foreground: 17 24 39; /* gray-900 RGB */ --ndpr-muted: 243 244 246; /* gray-100 RGB */ --ndpr-muted-foreground: 107 114 128; /* gray-500 RGB */ --ndpr-border: 229 231 235; /* gray-200 RGB */ --ndpr-input: 229 231 235; --ndpr-ring: 37 99 235; --ndpr-success: 22 163 74; /* green-600 RGB */ --ndpr-destructive: 220 38 38; /* red-600 RGB */ --ndpr-warning: 217 119 6; /* amber-600 RGB */ /* Spacing scale */ --ndpr-space-1: 0.25rem; --ndpr-space-2: 0.5rem; --ndpr-space-3: 0.75rem; --ndpr-space-4: 1rem; --ndpr-space-5: 1.25rem; --ndpr-space-6: 1.5rem; --ndpr-space-8: 2rem; /* Radii */ --ndpr-radius-sm: 0.25rem; --ndpr-radius: 0.5rem; --ndpr-radius-lg: 0.75rem; --ndpr-radius-full: 9999px; /* Shadows */ --ndpr-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); --ndpr-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.06); --ndpr-shadow-lg: 0 10px 25px -5px rgba(0, 0, 0, 0.10), 0 8px 10px -6px rgba(0, 0, 0, 0.08); /* Typography */ --ndpr-font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; --ndpr-font-size-xs: 0.75rem; --ndpr-font-size-sm: 0.875rem; --ndpr-font-size-base: 1rem; --ndpr-font-size-lg: 1.125rem; --ndpr-font-size-xl: 1.25rem; --ndpr-line-height: 1.5; --ndpr-line-height-tight: 1.25; /* Motion */ --ndpr-transition: 150ms ease-out; --ndpr-transition-slow: 300ms ease-out; /* Z-index */ --ndpr-z-banner: 9999; --ndpr-z-modal: 10000; /* Layout */ --ndpr-max-width: 72rem; } /* Dark theme — both `prefers-color-scheme: dark` and explicit opt-in via * the `data-theme="dark"` attribute or `.dark` class are supported. */ @media (prefers-color-scheme: dark) { :root { --ndpr-primary: 96 165 250; --ndpr-primary-hover: 147 197 253; --ndpr-background: 17 24 39; --ndpr-surface: 31 41 55; --ndpr-foreground: 243 244 246; --ndpr-muted: 55 65 81; --ndpr-muted-foreground: 156 163 175; --ndpr-border: 75 85 99; --ndpr-input: 75 85 99; --ndpr-ring: 96 165 250; } } [data-theme="dark"], .dark { --ndpr-primary: 96 165 250; --ndpr-primary-hover: 147 197 253; --ndpr-background: 17 24 39; --ndpr-surface: 31 41 55; --ndpr-foreground: 243 244 246; --ndpr-muted: 55 65 81; --ndpr-muted-foreground: 156 163 175; --ndpr-border: 75 85 99; --ndpr-input: 75 85 99; --ndpr-ring: 96 165 250; } [data-theme="light"] { --ndpr-primary: 37 99 235; --ndpr-primary-hover: 29 78 216; --ndpr-background: 255 255 255; --ndpr-surface: 249 250 251; --ndpr-foreground: 17 24 39; --ndpr-muted: 243 244 246; --ndpr-muted-foreground: 107 114 128; --ndpr-border: 229 231 235; --ndpr-input: 229 231 235; --ndpr-ring: 37 99 235; } /* ── Base / cross-cutting ──────────────────────────────────────────────── */ [data-ndpr-component] { font-family: var(--ndpr-font-sans); font-size: var(--ndpr-font-size-base); line-height: var(--ndpr-line-height); color: rgb(var(--ndpr-foreground)); box-sizing: border-box; } [data-ndpr-component] *, [data-ndpr-component] *::before, [data-ndpr-component] *::after { box-sizing: border-box; } [data-ndpr-component] :focus-visible { outline: 2px solid rgb(var(--ndpr-ring)); outline-offset: 2px; border-radius: var(--ndpr-radius-sm); } /* ── Animations ────────────────────────────────────────────────────────── */ @keyframes ndpr-slide-in-bottom { from { transform: translateY(100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } } @keyframes ndpr-slide-in-top { from { transform: translateY(-100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } } @keyframes ndpr-fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes ndpr-scale-in { from { transform: scale(0.96); opacity: 0; } to { transform: scale(1); opacity: 1; } } /* Legacy alias — kept for backward compat with any consumer relying on the * pre-3.5 animation classes shipped via animations.css. */ .animate-slide-in { animation: ndpr-slide-in-bottom var(--ndpr-transition-slow); } .animate-slide-in-top { animation: ndpr-slide-in-top var(--ndpr-transition-slow); } .animate-fade-in { animation: ndpr-fade-in var(--ndpr-transition-slow); } /* ── ConsentBanner ─────────────────────────────────────────────────────── */ .ndpr-consent-banner { background: rgb(var(--ndpr-background)); color: rgb(var(--ndpr-foreground)); border: 1px solid rgb(var(--ndpr-border)); box-shadow: var(--ndpr-shadow-lg); padding: var(--ndpr-space-4); animation: ndpr-fade-in var(--ndpr-transition-slow); } @media (min-width: 640px) { .ndpr-consent-banner { padding: var(--ndpr-space-6); } } /* Position modifiers */ .ndpr-consent-banner--bottom, .ndpr-consent-banner--top { position: fixed; inset-inline: 0; z-index: var(--ndpr-z-banner); border-radius: 0; } .ndpr-consent-banner--bottom { bottom: 0; border-bottom: none; border-inline-start: none; border-inline-end: none; animation: ndpr-slide-in-bottom var(--ndpr-transition-slow); } .ndpr-consent-banner--top { top: 0; border-top: none; border-inline-start: none; border-inline-end: none; animation: ndpr-slide-in-top var(--ndpr-transition-slow); } .ndpr-consent-banner--inline { border-radius: var(--ndpr-radius-lg); } /* Variant modifiers */ .ndpr-consent-banner--card { border-radius: var(--ndpr-radius-lg); margin: var(--ndpr-space-4); max-width: 32rem; } .ndpr-consent-banner--card.ndpr-consent-banner--bottom { bottom: var(--ndpr-space-4); inset-inline-start: auto; inset-inline-end: var(--ndpr-space-4); } .ndpr-consent-banner--card.ndpr-consent-banner--top { top: var(--ndpr-space-4); inset-inline-start: auto; inset-inline-end: var(--ndpr-space-4); } .ndpr-consent-banner--modal { border-radius: var(--ndpr-radius-lg); max-width: 32rem; margin: 0; animation: ndpr-scale-in var(--ndpr-transition-slow); } .ndpr-consent-banner__overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); z-index: var(--ndpr-z-modal); display: flex; align-items: center; justify-content: center; padding: var(--ndpr-space-4); animation: ndpr-fade-in var(--ndpr-transition); } .ndpr-consent-banner__container { max-width: var(--ndpr-max-width); margin: 0 auto; } .ndpr-consent-banner__title { margin: 0 0 var(--ndpr-space-2); font-size: var(--ndpr-font-size-lg); font-weight: 700; line-height: var(--ndpr-line-height-tight); color: rgb(var(--ndpr-foreground)); } .ndpr-consent-banner__description { margin: 0 0 var(--ndpr-space-4); font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-muted-foreground)); } @media (min-width: 640px) { .ndpr-consent-banner__description { font-size: var(--ndpr-font-size-base); } } .ndpr-consent-banner__customize-panel { margin-bottom: var(--ndpr-space-4); padding: var(--ndpr-space-4); background: rgb(var(--ndpr-surface)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); overflow: hidden; transition: max-height var(--ndpr-transition-slow), opacity var(--ndpr-transition-slow); } .ndpr-consent-banner__select-all-row { display: flex; justify-content: flex-end; margin-bottom: var(--ndpr-space-3); } .ndpr-consent-banner__select-all-button { background: transparent; border: 0; padding: 0; font-size: var(--ndpr-font-size-sm); font-weight: 600; color: rgb(var(--ndpr-primary)); cursor: pointer; } .ndpr-consent-banner__select-all-button:hover { text-decoration: underline; } .ndpr-consent-banner__options-list { display: flex; flex-direction: column; gap: var(--ndpr-space-3); margin: 0; padding: 0; list-style: none; } .ndpr-consent-banner__option { display: flex; align-items: flex-start; gap: var(--ndpr-space-3); } .ndpr-consent-banner__option-checkbox { width: 1rem; height: 1rem; margin-top: 0.2rem; accent-color: rgb(var(--ndpr-primary)); flex-shrink: 0; cursor: pointer; } .ndpr-consent-banner__option-checkbox:disabled { cursor: not-allowed; opacity: 0.6; } .ndpr-consent-banner__option-text { flex: 1; font-size: var(--ndpr-font-size-sm); } .ndpr-consent-banner__option-label { display: block; font-weight: 600; color: rgb(var(--ndpr-foreground)); cursor: pointer; } .ndpr-consent-banner__required-marker { color: rgb(var(--ndpr-destructive)); margin-inline-start: var(--ndpr-space-1); } .ndpr-consent-banner__option-description { margin: var(--ndpr-space-1) 0 0; color: rgb(var(--ndpr-muted-foreground)); font-size: var(--ndpr-font-size-sm); line-height: var(--ndpr-line-height); } .ndpr-consent-banner__buttons { display: flex; flex-direction: column; gap: var(--ndpr-space-2); margin-top: var(--ndpr-space-4); } @media (min-width: 640px) { .ndpr-consent-banner__buttons { flex-direction: row; flex-wrap: wrap; } } .ndpr-consent-banner__button { appearance: none; display: inline-flex; align-items: center; justify-content: center; padding: var(--ndpr-space-2) var(--ndpr-space-4); min-height: 44px; border: 1px solid transparent; border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); font-weight: 600; font-family: inherit; line-height: 1.25; cursor: pointer; transition: background var(--ndpr-transition), border-color var(--ndpr-transition), color var(--ndpr-transition); user-select: none; } .ndpr-consent-banner__button:disabled { cursor: not-allowed; opacity: 0.6; } .ndpr-consent-banner__button--primary { background: rgb(var(--ndpr-primary)); color: rgb(var(--ndpr-primary-foreground)); } .ndpr-consent-banner__button--primary:hover:not(:disabled) { background: rgb(var(--ndpr-primary-hover)); } .ndpr-consent-banner__button--secondary { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); border-color: rgb(var(--ndpr-border)); } .ndpr-consent-banner__button--secondary:hover:not(:disabled) { background: rgb(var(--ndpr-border)); } .ndpr-consent-banner__button--ghost { background: transparent; color: rgb(var(--ndpr-foreground)); } .ndpr-consent-banner__button--ghost:hover:not(:disabled) { background: rgb(var(--ndpr-muted)); } .ndpr-consent-banner__footer-text { margin-top: var(--ndpr-space-2); font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-muted-foreground)); } /* ── ConsentManager (settings-page surface) ────────────────────────────── */ .ndpr-consent-manager { background: rgb(var(--ndpr-background)); color: rgb(var(--ndpr-foreground)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius-lg); box-shadow: var(--ndpr-shadow); padding: var(--ndpr-space-6); } .ndpr-consent-manager__title { margin: 0 0 var(--ndpr-space-2); font-size: var(--ndpr-font-size-xl); font-weight: 700; line-height: var(--ndpr-line-height-tight); } .ndpr-consent-manager__description { margin: 0 0 var(--ndpr-space-6); color: rgb(var(--ndpr-muted-foreground)); font-size: var(--ndpr-font-size-sm); } .ndpr-consent-manager__options-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; } .ndpr-consent-manager__option { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--ndpr-space-4); padding: var(--ndpr-space-4) 0; border-bottom: 1px solid rgb(var(--ndpr-border)); } .ndpr-consent-manager__option:last-child { border-bottom: 0; } .ndpr-consent-manager__option-info { flex: 1; min-width: 0; } .ndpr-consent-manager__option-label { margin: 0; font-weight: 600; font-size: var(--ndpr-font-size-base); color: rgb(var(--ndpr-foreground)); } .ndpr-consent-manager__option-description { margin: var(--ndpr-space-1) 0 0; color: rgb(var(--ndpr-muted-foreground)); font-size: var(--ndpr-font-size-sm); } .ndpr-consent-manager__toggle-wrapper { display: inline-flex; align-items: center; gap: var(--ndpr-space-3); cursor: pointer; flex-shrink: 0; } /* Pure-CSS switch — checkbox visually hidden, label fakes the toggle. */ .ndpr-consent-manager__toggle-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } .ndpr-consent-manager__toggle { position: relative; width: 2.75rem; height: 1.5rem; background: rgb(var(--ndpr-muted)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius-full); transition: background var(--ndpr-transition); flex-shrink: 0; } .ndpr-consent-manager__toggle::after { content: ''; position: absolute; top: 1px; /* Physical `left` kept (paired with `translateX` below); switching to * inset-inline-start without a logical transform would desync the resting * position from the checked-state translation in RTL contexts. */ left: 1px; width: 1.25rem; height: 1.25rem; background: rgb(var(--ndpr-background)); border-radius: 50%; box-shadow: var(--ndpr-shadow-sm); transition: transform var(--ndpr-transition); } .ndpr-consent-manager__toggle-input:checked + .ndpr-consent-manager__toggle { background: rgb(var(--ndpr-primary)); border-color: rgb(var(--ndpr-primary)); } .ndpr-consent-manager__toggle-input:checked + .ndpr-consent-manager__toggle::after { transform: translateX(1.25rem); } .ndpr-consent-manager__toggle-input:focus-visible + .ndpr-consent-manager__toggle { outline: 2px solid rgb(var(--ndpr-ring)); outline-offset: 2px; } .ndpr-consent-manager__toggle-input:disabled + .ndpr-consent-manager__toggle { opacity: 0.6; cursor: not-allowed; } .ndpr-consent-manager__toggle-status { font-size: var(--ndpr-font-size-sm); font-weight: 500; color: rgb(var(--ndpr-foreground)); } .ndpr-consent-manager__required-marker { margin-inline-start: var(--ndpr-space-1); font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-destructive)); } .ndpr-consent-manager__success { margin-top: var(--ndpr-space-4); padding: var(--ndpr-space-3) var(--ndpr-space-4); background: rgb(var(--ndpr-success) / 0.12); color: rgb(var(--ndpr-success)); border: 1px solid rgb(var(--ndpr-success) / 0.3); border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); } .ndpr-consent-manager__buttons { margin-top: var(--ndpr-space-6); display: flex; flex-wrap: wrap; gap: var(--ndpr-space-3); } .ndpr-consent-manager__button { appearance: none; display: inline-flex; align-items: center; justify-content: center; padding: var(--ndpr-space-2) var(--ndpr-space-4); border: 1px solid transparent; border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); font-weight: 600; font-family: inherit; line-height: 1.25; cursor: pointer; transition: background var(--ndpr-transition), border-color var(--ndpr-transition); } .ndpr-consent-manager__button--primary { background: rgb(var(--ndpr-primary)); color: rgb(var(--ndpr-primary-foreground)); } .ndpr-consent-manager__button--primary:hover:not(:disabled) { background: rgb(var(--ndpr-primary-hover)); } .ndpr-consent-manager__button--secondary { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); border-color: rgb(var(--ndpr-border)); } .ndpr-consent-manager__button--secondary:hover:not(:disabled) { background: rgb(var(--ndpr-border)); } .ndpr-consent-manager__meta { margin-top: var(--ndpr-space-4); display: flex; flex-wrap: wrap; gap: var(--ndpr-space-1) var(--ndpr-space-4); font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-muted-foreground)); } .ndpr-consent-manager__meta p { margin: 0; } /* ── Form primitives (shared by DSR / Breach / DPIA / ROPA forms) ──────── */ .ndpr-form-field { display: flex; flex-direction: column; gap: var(--ndpr-space-1); } .ndpr-form-field__label { display: block; font-size: var(--ndpr-font-size-sm); font-weight: 500; color: rgb(var(--ndpr-foreground)); } .ndpr-form-field__required { margin-inline-start: var(--ndpr-space-1); color: rgb(var(--ndpr-destructive)); } .ndpr-form-field__input, .ndpr-form-field__select, .ndpr-form-field__textarea { width: 100%; padding: var(--ndpr-space-2) var(--ndpr-space-3); font-size: var(--ndpr-font-size-base); font-family: inherit; color: rgb(var(--ndpr-foreground)); background: rgb(var(--ndpr-background)); border: 1px solid rgb(var(--ndpr-input)); border-radius: var(--ndpr-radius); transition: border-color var(--ndpr-transition), box-shadow var(--ndpr-transition); outline: none; appearance: none; } .ndpr-form-field__textarea { min-height: 6rem; resize: vertical; line-height: var(--ndpr-line-height); } .ndpr-form-field__input:focus, .ndpr-form-field__select:focus, .ndpr-form-field__textarea:focus { border-color: rgb(var(--ndpr-primary)); box-shadow: 0 0 0 3px rgb(var(--ndpr-ring) / 0.25); } .ndpr-form-field__input:disabled, .ndpr-form-field__select:disabled, .ndpr-form-field__textarea:disabled { background: rgb(var(--ndpr-muted)); cursor: not-allowed; } .ndpr-form-field__input[aria-invalid="true"], .ndpr-form-field__select[aria-invalid="true"], .ndpr-form-field__textarea[aria-invalid="true"] { border-color: rgb(var(--ndpr-destructive)); } .ndpr-form-field__input[aria-invalid="true"]:focus, .ndpr-form-field__select[aria-invalid="true"]:focus, .ndpr-form-field__textarea[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgb(var(--ndpr-destructive) / 0.25); } .ndpr-form-field__error { margin: var(--ndpr-space-1) 0 0; font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-destructive)); } .ndpr-form-field__hint { margin: var(--ndpr-space-1) 0 0; font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-muted-foreground)); } .ndpr-form-field__checkbox-row { display: flex; align-items: flex-start; gap: var(--ndpr-space-3); } .ndpr-form-field__checkbox, .ndpr-form-field__radio { width: 1rem; height: 1rem; margin-top: 0.2rem; accent-color: rgb(var(--ndpr-primary)); flex-shrink: 0; cursor: pointer; } .ndpr-form-field__option-group { display: flex; flex-direction: column; gap: var(--ndpr-space-2); } .ndpr-form-field__input--mono { font-family: var(--ndpr-font-mono, ui-monospace, 'Cascadia Code', Menlo, monospace); font-size: var(--ndpr-font-size-sm); } .ndpr-form-section { display: flex; flex-direction: column; gap: var(--ndpr-space-4); } .ndpr-form-section__heading { margin: 0 0 var(--ndpr-space-3); font-size: var(--ndpr-font-size-lg); font-weight: 600; color: rgb(var(--ndpr-foreground)); } .ndpr-form-section__hint { margin: 0 0 var(--ndpr-space-3); font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-muted-foreground)); } .ndpr-form-grid { display: grid; grid-template-columns: 1fr; gap: var(--ndpr-space-4); } @media (min-width: 640px) { .ndpr-form-grid--2 { grid-template-columns: 1fr 1fr; } } /* ── DSRRequestForm ────────────────────────────────────────────────────── */ .ndpr-dsr-form { background: rgb(var(--ndpr-background)); color: rgb(var(--ndpr-foreground)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius-lg); box-shadow: var(--ndpr-shadow); padding: var(--ndpr-space-6); } .ndpr-dsr-form__title { margin: 0 0 var(--ndpr-space-2); font-size: var(--ndpr-font-size-xl); font-weight: 700; line-height: var(--ndpr-line-height-tight); } .ndpr-dsr-form__description { margin: 0 0 var(--ndpr-space-6); color: rgb(var(--ndpr-muted-foreground)); font-size: var(--ndpr-font-size-sm); } .ndpr-dsr-form__sections { display: flex; flex-direction: column; gap: var(--ndpr-space-6); } .ndpr-dsr-form__type-info { margin: var(--ndpr-space-3) 0; padding: var(--ndpr-space-3) var(--ndpr-space-4); background: rgb(var(--ndpr-surface)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-muted-foreground)); } .ndpr-dsr-form__type-info p { margin: 0; } .ndpr-dsr-form__type-info p + p { margin-top: var(--ndpr-space-1); } .ndpr-dsr-form__notice { margin-top: var(--ndpr-space-2); padding: var(--ndpr-space-4); background: rgb(var(--ndpr-surface)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); } .ndpr-dsr-form__notice-title { margin: 0 0 var(--ndpr-space-2); font-size: var(--ndpr-font-size-sm); font-weight: 600; } .ndpr-dsr-form__notice-body { margin: 0; font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-muted-foreground)); line-height: var(--ndpr-line-height); } .ndpr-dsr-form__actions { display: flex; flex-wrap: wrap; gap: var(--ndpr-space-3); margin-top: var(--ndpr-space-2); position: relative; } .ndpr-dsr-form__button { appearance: none; display: inline-flex; align-items: center; justify-content: center; padding: var(--ndpr-space-3) var(--ndpr-space-6); border: 1px solid transparent; border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); font-weight: 600; font-family: inherit; line-height: 1.25; cursor: pointer; transition: background var(--ndpr-transition), border-color var(--ndpr-transition); } .ndpr-dsr-form__button:disabled { opacity: 0.5; cursor: not-allowed; } .ndpr-dsr-form__button--primary { background: rgb(var(--ndpr-primary)); color: rgb(var(--ndpr-primary-foreground)); } .ndpr-dsr-form__button--primary:hover:not(:disabled) { background: rgb(var(--ndpr-primary-hover)); } .ndpr-dsr-form__button--secondary { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); border-color: rgb(var(--ndpr-border)); } .ndpr-dsr-form__button--secondary:hover:not(:disabled) { background: rgb(var(--ndpr-border)); } .ndpr-dsr-form__success { background: rgb(var(--ndpr-success) / 0.08); color: rgb(var(--ndpr-foreground)); border: 1px solid rgb(var(--ndpr-success) / 0.4); border-radius: var(--ndpr-radius); padding: var(--ndpr-space-6); } .ndpr-dsr-form__success-title { margin: 0 0 var(--ndpr-space-2); font-size: var(--ndpr-font-size-lg); font-weight: 700; color: rgb(var(--ndpr-success)); } .ndpr-dsr-form__success-body { margin: 0 0 var(--ndpr-space-4); color: rgb(var(--ndpr-foreground)); font-size: var(--ndpr-font-size-sm); } /* ── Shared layout primitives ──────────────────────────────────────────── */ /* Generic surface card — used by every "manager" / "report" / "dashboard" * component. Replaces the repeated * `bg-white dark:bg-gray-800 border ... rounded-lg p-{4,6} shadow-md` * pattern. */ .ndpr-card { background: rgb(var(--ndpr-background)); color: rgb(var(--ndpr-foreground)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius-lg); box-shadow: var(--ndpr-shadow); padding: var(--ndpr-space-6); } .ndpr-card--compact { padding: var(--ndpr-space-4); } .ndpr-card--flat { box-shadow: none; } .ndpr-card--subtle { background: rgb(var(--ndpr-surface)); box-shadow: none; padding: var(--ndpr-space-4); } .ndpr-card__header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--ndpr-space-4); margin-bottom: var(--ndpr-space-4); } .ndpr-card__title { margin: 0; font-size: var(--ndpr-font-size-lg); font-weight: 600; line-height: var(--ndpr-line-height-tight); } .ndpr-card__subtitle { margin: var(--ndpr-space-1) 0 0; font-size: var(--ndpr-font-size-sm); color: rgb(var(--ndpr-muted-foreground)); } .ndpr-card__body { display: flex; flex-direction: column; gap: var(--ndpr-space-4); } .ndpr-card__footer { display: flex; flex-wrap: wrap; gap: var(--ndpr-space-3); margin-top: var(--ndpr-space-6); padding-top: var(--ndpr-space-4); border-top: 1px solid rgb(var(--ndpr-border)); } /* Inline panel — light-surface inset for grouped content. */ .ndpr-panel { background: rgb(var(--ndpr-surface)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); padding: var(--ndpr-space-3) var(--ndpr-space-4); } /* ── Status badges (pills) ─────────────────────────────────────────────── */ .ndpr-badge { display: inline-flex; align-items: center; gap: var(--ndpr-space-1); padding: 0.125rem var(--ndpr-space-2); border-radius: var(--ndpr-radius-full); font-size: var(--ndpr-font-size-xs); font-weight: 600; line-height: 1.4; border: 1px solid transparent; background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); } .ndpr-badge--success { background: rgb(var(--ndpr-success) / 0.12); color: rgb(var(--ndpr-success)); border-color: rgb(var(--ndpr-success) / 0.3); } .ndpr-badge--warning { background: rgb(var(--ndpr-warning) / 0.12); color: rgb(var(--ndpr-warning)); border-color: rgb(var(--ndpr-warning) / 0.3); } .ndpr-badge--destructive, .ndpr-badge--danger { background: rgb(var(--ndpr-destructive) / 0.12); color: rgb(var(--ndpr-destructive)); border-color: rgb(var(--ndpr-destructive) / 0.3); } .ndpr-badge--info { background: rgb(var(--ndpr-primary) / 0.12); color: rgb(var(--ndpr-primary)); border-color: rgb(var(--ndpr-primary) / 0.3); } .ndpr-badge--neutral { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-muted-foreground)); border-color: rgb(var(--ndpr-border)); } /* ── Alert / callout blocks ────────────────────────────────────────────── */ .ndpr-alert { display: flex; flex-direction: column; gap: var(--ndpr-space-1); padding: var(--ndpr-space-3) var(--ndpr-space-4); border-radius: var(--ndpr-radius); border: 1px solid rgb(var(--ndpr-border)); background: rgb(var(--ndpr-surface)); color: rgb(var(--ndpr-foreground)); font-size: var(--ndpr-font-size-sm); } .ndpr-alert__title { margin: 0; font-weight: 600; } .ndpr-alert__body { margin: 0; color: rgb(var(--ndpr-muted-foreground)); } .ndpr-alert--info { background: rgb(var(--ndpr-primary) / 0.08); border-color: rgb(var(--ndpr-primary) / 0.3); } .ndpr-alert--info .ndpr-alert__title { color: rgb(var(--ndpr-primary)); } .ndpr-alert--success { background: rgb(var(--ndpr-success) / 0.08); border-color: rgb(var(--ndpr-success) / 0.3); } .ndpr-alert--success .ndpr-alert__title { color: rgb(var(--ndpr-success)); } .ndpr-alert--warning { background: rgb(var(--ndpr-warning) / 0.08); border-color: rgb(var(--ndpr-warning) / 0.3); } .ndpr-alert--warning .ndpr-alert__title { color: rgb(var(--ndpr-warning)); } .ndpr-alert--destructive, .ndpr-alert--danger { background: rgb(var(--ndpr-destructive) / 0.08); border-color: rgb(var(--ndpr-destructive) / 0.3); } .ndpr-alert--destructive .ndpr-alert__title, .ndpr-alert--danger .ndpr-alert__title { color: rgb(var(--ndpr-destructive)); } /* ── Tables ────────────────────────────────────────────────────────────── */ .ndpr-table { width: 100%; border-collapse: collapse; font-size: var(--ndpr-font-size-sm); } .ndpr-table__head { background: rgb(var(--ndpr-surface)); text-align: start; font-size: var(--ndpr-font-size-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: rgb(var(--ndpr-muted-foreground)); } .ndpr-table__row { border-bottom: 1px solid rgb(var(--ndpr-border)); } .ndpr-table__row:last-child { border-bottom: 0; } .ndpr-table__cell, .ndpr-table__heading { padding: var(--ndpr-space-3) var(--ndpr-space-4); vertical-align: top; } .ndpr-table__heading { font-weight: 600; } .ndpr-table__cell--muted { color: rgb(var(--ndpr-muted-foreground)); } .ndpr-table__cell--actions { text-align: end; white-space: nowrap; } .ndpr-table-wrapper { overflow-x: auto; border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); } /* ── Empty / placeholder states ────────────────────────────────────────── */ .ndpr-empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--ndpr-space-2); min-height: 12rem; padding: var(--ndpr-space-6); background: rgb(var(--ndpr-surface)); border: 1px dashed rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); color: rgb(var(--ndpr-muted-foreground)); text-align: center; } .ndpr-empty-state__title { margin: 0; font-size: var(--ndpr-font-size-base); font-weight: 600; color: rgb(var(--ndpr-foreground)); } .ndpr-empty-state__body { margin: 0; font-size: var(--ndpr-font-size-sm); } /* ── Range / slider input ──────────────────────────────────────────────── */ .ndpr-form-field__range { width: 100%; height: 0.5rem; background: rgb(var(--ndpr-muted)); border-radius: var(--ndpr-radius-full); appearance: none; cursor: pointer; outline: none; } .ndpr-form-field__range::-webkit-slider-thumb { appearance: none; width: 1.125rem; height: 1.125rem; border-radius: 50%; background: rgb(var(--ndpr-primary)); border: 2px solid rgb(var(--ndpr-background)); box-shadow: var(--ndpr-shadow-sm); cursor: pointer; } .ndpr-form-field__range::-moz-range-thumb { width: 1.125rem; height: 1.125rem; border-radius: 50%; background: rgb(var(--ndpr-primary)); border: 2px solid rgb(var(--ndpr-background)); box-shadow: var(--ndpr-shadow-sm); cursor: pointer; } /* ── Generic button family (used across non-consent components) ────────── */ .ndpr-button { appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: var(--ndpr-space-2); padding: var(--ndpr-space-2) var(--ndpr-space-4); min-height: 44px; border: 1px solid transparent; border-radius: var(--ndpr-radius); font-size: var(--ndpr-font-size-sm); font-weight: 600; font-family: inherit; line-height: 1.25; cursor: pointer; transition: background var(--ndpr-transition), border-color var(--ndpr-transition), color var(--ndpr-transition); text-decoration: none; } .ndpr-button:disabled { opacity: 0.5; cursor: not-allowed; } .ndpr-button--primary { background: rgb(var(--ndpr-primary)); color: rgb(var(--ndpr-primary-foreground)); } .ndpr-button--primary:hover:not(:disabled) { background: rgb(var(--ndpr-primary-hover)); } .ndpr-button--secondary { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); border-color: rgb(var(--ndpr-border)); } .ndpr-button--secondary:hover:not(:disabled) { background: rgb(var(--ndpr-border)); } .ndpr-button--ghost { background: transparent; color: rgb(var(--ndpr-foreground)); } .ndpr-button--ghost:hover:not(:disabled) { background: rgb(var(--ndpr-muted)); } .ndpr-button--destructive { background: rgb(var(--ndpr-destructive)); color: #fff; } .ndpr-button--destructive:hover:not(:disabled) { filter: brightness(0.92); } .ndpr-button--sm { padding: var(--ndpr-space-1) var(--ndpr-space-3); font-size: var(--ndpr-font-size-xs); } .ndpr-button--lg { padding: var(--ndpr-space-3) var(--ndpr-space-6); font-size: var(--ndpr-font-size-base); } .ndpr-button--icon { padding: var(--ndpr-space-2); background: transparent; color: rgb(var(--ndpr-muted-foreground)); } .ndpr-button--icon:hover:not(:disabled) { background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-foreground)); } /* ── Step indicator (DPIA / Policy wizards) ────────────────────────────── */ .ndpr-step-indicator { display: flex; align-items: flex-start; gap: var(--ndpr-space-2); width: 100%; margin-bottom: var(--ndpr-space-6); } .ndpr-step-indicator__item { flex: 1; display: flex; flex-direction: column; align-items: center; text-align: center; position: relative; min-width: 0; } .ndpr-step-indicator__item:not(:last-child)::after { content: ''; position: absolute; top: 1rem; inset-inline-start: 50%; width: 100%; height: 2px; background: rgb(var(--ndpr-border)); z-index: 0; } .ndpr-step-indicator__item--complete:not(:last-child)::after { background: rgb(var(--ndpr-primary)); } .ndpr-step-indicator__bullet { position: relative; z-index: 1; width: 2rem; height: 2rem; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: var(--ndpr-font-size-sm); font-weight: 600; background: rgb(var(--ndpr-muted)); color: rgb(var(--ndpr-muted-foreground)); border: 2px solid rgb(var(--ndpr-border)); } .ndpr-step-indicator__item--current .ndpr-step-indicator__bullet { background: rgb(var(--ndpr-primary)); color: rgb(var(--ndpr-primary-foreground)); border-color: rgb(var(--ndpr-primary)); } .ndpr-step-indicator__item--complete .ndpr-step-indicator__bullet { background: rgb(var(--ndpr-success)); color: #fff; border-color: rgb(var(--ndpr-success)); } .ndpr-step-indicator__label { margin-top: var(--ndpr-space-2); font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-muted-foreground)); font-weight: 500; } .ndpr-step-indicator__item--current .ndpr-step-indicator__label { color: rgb(var(--ndpr-foreground)); font-weight: 600; } /* ── Progress bar ──────────────────────────────────────────────────────── */ .ndpr-progress { width: 100%; height: 0.5rem; background: rgb(var(--ndpr-muted)); border-radius: var(--ndpr-radius-full); overflow: hidden; } .ndpr-progress__bar { height: 100%; background: rgb(var(--ndpr-primary)); transition: width var(--ndpr-transition-slow); } .ndpr-progress__bar--success { background: rgb(var(--ndpr-success)); } .ndpr-progress__bar--warning { background: rgb(var(--ndpr-warning)); } .ndpr-progress__bar--destructive { background: rgb(var(--ndpr-destructive)); } /* ── Stat grid (NDPRDashboard, compliance reports) ─────────────────────── */ .ndpr-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: var(--ndpr-space-4); } .ndpr-stat { background: rgb(var(--ndpr-surface)); border: 1px solid rgb(var(--ndpr-border)); border-radius: var(--ndpr-radius); padding: var(--ndpr-space-4); } .ndpr-stat__label { margin: 0 0 var(--ndpr-space-1); font-size: var(--ndpr-font-size-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: rgb(var(--ndpr-muted-foreground)); } .ndpr-stat__value { margin: 0; font-size: var(--ndpr-font-size-xl); font-weight: 700; color: rgb(var(--ndpr-foreground)); line-height: var(--ndpr-line-height-tight); } .ndpr-stat__delta { margin: var(--ndpr-space-1) 0 0; font-size: var(--ndpr-font-size-xs); color: rgb(var(--ndpr-muted-foreground)); } /* ── Section heading (used inside cards) ───────────────────────────────── */ .ndpr-section-heading { margin: 0 0 var(--ndpr-space-3); font-size: var(--ndpr-font-size-base); font-weight: 600; color: rgb(var(--ndpr-foreground)); } .ndpr-section-divider { height: 1px; background: rgb(var(--ndpr-border)); border: 0; margin: var(--ndpr-space-6) 0; } /* ── Semantic text utilities ───────────────────────────────────────────── */ .ndpr-text-muted { color: rgb(var(--ndpr-muted-foreground)); } .ndpr-text-foreground { color: rgb(var(--ndpr-foreground)); } .ndpr-text-primary { color: rgb(var(--ndpr-primary)); } .ndpr-text-success { color: rgb(var(--ndpr-success)); } .ndpr-text-warning { color: rgb(var(--ndpr-warning)); } .ndpr-text-destructive { color: rgb(var(--ndpr-destructive)); } .ndpr-text-info { color: rgb(var(--ndpr-primary)); } .ndpr-text-xs { font-size: var(--ndpr-font-size-xs); } .ndpr-text-sm { font-size: var(--ndpr-font-size-sm); } .ndpr-text-base { font-size: var(--ndpr-font-size-base); } .ndpr-text-lg { font-size: var(--ndpr-font-size-lg); } .ndpr-text-xl { font-size: var(--ndpr-font-size-xl); } .ndpr-font-medium { font-weight: 500; } .ndpr-font-semibold { font-weight: 600; } .ndpr-font-bold { font-weight: 700; } /* ========================================================================== * Accessibility — respect prefers-reduced-motion (WCAG 2.3.3) * * Users with vestibular-motion sensitivities can opt out of animation at the * OS level. When they do, neutralise every animation and transition the * toolkit ships so banners, dialogs, dashboards, and policy previews appear * without movement. Applies to all toolkit elements via the `[data-ndpr-*]` * attribute hooks that components emit, but we also widen to '*' below * because not every animated element carries one. * ========================================================================== */ @media (prefers-reduced-motion: reduce) { .ndpr-banner, .ndpr-modal, .ndpr-modal__panel, .ndpr-modal__overlay, .ndpr-dialog, .ndpr-consent-banner, .ndpr-consent-manager, .ndpr-dsr-dashboard, .ndpr-dpia-questionnaire, .ndpr-breach-form, .ndpr-policy-preview, .animate-slide-in, .animate-slide-in-top, .animate-fade-in, [class*="ndpr-"] *, [data-ndpr-legal-notice] { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; } }