{"_id":"@drkaachen/design-system-ui","_rev":"6-c4ab25342dd51e7cddedfe4b05c96814","name":"@drkaachen/design-system-ui","dist-tags":{"latest":"1.2.1"},"versions":{"1.0.0":{"name":"@drkaachen/design-system-ui","version":"1.0.0","license":"MIT","_id":"@drkaachen/design-system-ui@1.0.0","maintainers":[{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"homepage":"https://github.com/DRKAachen/drk-design-system#readme","bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"dist":{"shasum":"d88c6887aa70820ebc2d5a6ab4260f5507d1a56a","tarball":"https://registry.npmjs.org/@drkaachen/design-system-ui/-/design-system-ui-1.0.0.tgz","fileCount":44,"integrity":"sha512-W/N/agb65QPLlJwLl3SeDWuy8BPSvxvzE+BjRnr2/xLSrjVVV5NYtVuNtuWCqtjAbH7nieCiuzQ0QXiAD646uQ==","signatures":[{"sig":"MEUCIQCcLqIxFPJrkS2tUvs9CNaiTmJDQSyKFF+ESUvhlPwnKgIgA7A+yv0FZiSQw1FEHdRZAwO9xwy5+Rt51LIXiZ6zfk4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drkaachen%2fdesign-system-ui@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":89651},"main":"./index.ts","types":"./index.ts","engines":{"node":">=20.0.0"},"exports":{".":"./index.ts","./lib/*":"./lib/*","./styles/*":"./styles/*","./components/*":"./components/*","./components/*/*":"./components/*/*","./components/Button/Button":"./components/Button/Button.tsx"},"gitHead":"fc20e105625334e100506235bbb40f9cbf62013a","private":false,"scripts":{"test":"vitest run","typecheck":"tsc --noEmit","test:workspaces":"npm run test --workspaces --if-present","typecheck:workspaces":"npm run typecheck --workspaces --if-present"},"_npmUser":{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"},"repository":{"url":"git+https://github.com/DRKAachen/drk-design-system.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.9.0","description":"DRK shared UI design system for Next.js projects","directories":{},"_nodeVersion":"24.14.0","dependencies":{"sanitize-html":"^2.17.0","@fontsource/merriweather":"^5.2.11"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sass":"^1.77.0","vitest":"^4.0.18","typescript":"^5.6.0","@types/node":"^25.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/sanitize-html":"^2.16.0"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/design-system-ui_1.0.0_1772713141648_0.449116797044371","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@drkaachen/design-system-ui","version":"1.0.1","license":"MIT","_id":"@drkaachen/design-system-ui@1.0.1","maintainers":[{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"homepage":"https://github.com/DRKAachen/drk-design-system#readme","bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"dist":{"shasum":"c7b473d51945f6b51b82c33e15308c1a87eff25c","tarball":"https://registry.npmjs.org/@drkaachen/design-system-ui/-/design-system-ui-1.0.1.tgz","fileCount":44,"integrity":"sha512-EXo+0+tKCfhoyNsQ3eTR63bDKtBqyGRA1QtbVjoIzvQXQUjSzstt6Jz8Qe1VW+ZwxiiWe8g1lyBYnNS2ZP4zQQ==","signatures":[{"sig":"MEQCIBJIk4ZDr40momsR0CFHBGC9diLlwH8ecRD5tIB3/o2LAiAsXKPfb2knV4xO2khYDUIXH+0Yd/KwO/yY3JVOiQMblg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drkaachen%2fdesign-system-ui@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":89643},"main":"./index.ts","types":"./index.ts","engines":{"node":">=20.0.0"},"exports":{".":"./index.ts","./lib/*":"./lib/*","./styles/*":"./styles/*","./components/*":"./components/*","./components/*/*":"./components/*/*","./components/Button/Button":"./components/Button/Button.tsx"},"gitHead":"46a94dba99e32a65d90d2f45e680305ffeb2670f","private":false,"scripts":{"test":"vitest run","typecheck":"tsc --noEmit","test:workspaces":"npm run test --workspaces --if-present","typecheck:workspaces":"npm run typecheck --workspaces --if-present"},"_npmUser":{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"},"repository":{"url":"git+https://github.com/DRKAachen/drk-design-system.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.9.0","description":"DRK shared UI design system for Next.js projects","directories":{},"_nodeVersion":"24.14.0","dependencies":{"sanitize-html":"^2.17.0","@fontsource/merriweather":"^5.2.11"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sass":"^1.77.0","vitest":"^4.0.18","typescript":"^5.6.0","@types/node":"^25.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/sanitize-html":"^2.16.0"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/design-system-ui_1.0.1_1772714115792_0.949066973554215","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@drkaachen/design-system-ui","version":"1.1.0","license":"MIT","_id":"@drkaachen/design-system-ui@1.1.0","maintainers":[{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"homepage":"https://github.com/DRKAachen/drk-design-system#readme","bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"dist":{"shasum":"57dbebd75b6d5db5d6b4039e86147dac3c73b1ad","tarball":"https://registry.npmjs.org/@drkaachen/design-system-ui/-/design-system-ui-1.1.0.tgz","fileCount":46,"integrity":"sha512-WpQDIqGt3CgP6lVy4auqY0w9WaCCEsCfXjXXwKIHZHQGeBo5ywsasN+Gz0BnmCYSGLBGA9ML5OkMIZ+QQTCCkQ==","signatures":[{"sig":"MEYCIQC7mSb74C6gc+SKV3jG3flIe0Vue5ZJkoKpIHfxIt0dzwIhAInCVZ40nSHgU3frxQ/kqExqlKbi3L+Ovs1h5Oet0U0t","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drkaachen%2fdesign-system-ui@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":108411},"main":"./index.ts","type":"module","types":"./index.ts","engines":{"node":">=20.0.0"},"exports":{".":"./index.ts","./lib/*":"./lib/*","./styles/*":"./styles/*","./components/*":"./components/*","./components/*/*":"./components/*/*","./components/Button/Button":"./components/Button/Button.tsx"},"gitHead":"bc0fb90dfbdb447eec06e830360477af0805f052","private":false,"scripts":{"lint":"eslint .","test":"vitest run","format":"prettier --write .","prepare":"husky","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","format:check":"prettier --check .","test:workspaces":"npm run test --workspaces --if-present","typecheck:workspaces":"npm run typecheck --workspaces --if-present"},"_npmUser":{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"},"repository":{"url":"git+https://github.com/DRKAachen/drk-design-system.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.9.0","description":"DRK shared UI design system for Next.js projects","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"],"*.{scss,css,json,yml,yaml}":["prettier --write"]},"_nodeVersion":"24.14.0","dependencies":{"sanitize-html":"^2.17.0","@fontsource/merriweather":"^5.2.11"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sass":"^1.77.0","husky":"^9.1.0","jsdom":"^26.0.0","eslint":"^9.39.0","vitest":"^4.0.18","prettier":"^3.0.0","@eslint/js":"^9.0.0","typescript":"^5.6.0","@types/node":"^25.3.3","lint-staged":"^16.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","typescript-eslint":"^8.56.1","eslint-plugin-react":"^7.37.0","@types/sanitize-html":"^2.16.0","@testing-library/react":"^16.3.0","eslint-config-prettier":"^10.1.0","eslint-plugin-jsx-a11y":"^6.10.0","@testing-library/jest-dom":"^6.0.0","eslint-plugin-react-hooks":"^5.0.0","@testing-library/user-event":"^14.6.1"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/design-system-ui_1.1.0_1772727089552_0.5386609193512664","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@drkaachen/design-system-ui","version":"1.1.1","license":"MIT","_id":"@drkaachen/design-system-ui@1.1.1","maintainers":[{"name":"afielen","email":"axel.fielen@drk-aachen.de"},{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"homepage":"https://github.com/DRKAachen/drk-design-system#readme","bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"dist":{"shasum":"354e5f78984dabd135f8741c769967eb937415a9","tarball":"https://registry.npmjs.org/@drkaachen/design-system-ui/-/design-system-ui-1.1.1.tgz","fileCount":46,"integrity":"sha512-W9r/1X3ZI3Ruj2ByLi2SdYmNuhCCPxCHDQPAS7Tl+sCasI0qY0wz0C/djPwpC3t73PjvYNvhvF1XVVNsUoLBqQ==","signatures":[{"sig":"MEYCIQCfS5GpVb2qOnD9ylF37rc47O8WWkKS3PGhitPIyVcoIgIhAIRq59TiCbS2rfRoZckHJyC1WNTnry7eProQoIWiVr4m","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drkaachen%2fdesign-system-ui@1.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":108411},"main":"./index.ts","type":"module","types":"./index.ts","engines":{"node":">=20.0.0"},"exports":{".":"./index.ts","./lib/*":"./lib/*","./styles/*":"./styles/*","./components/*":"./components/*","./components/*/*":"./components/*/*","./components/Button/Button":"./components/Button/Button.tsx"},"gitHead":"0995b7523a95586042332ad00ab4f0ce59e9e302","private":false,"scripts":{"lint":"eslint .","test":"vitest run","format":"prettier --write .","prepare":"husky","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","format:check":"prettier --check .","test:workspaces":"npm run test --workspaces --if-present","typecheck:workspaces":"npm run typecheck --workspaces --if-present"},"_npmUser":{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"},"repository":{"url":"git+https://github.com/DRKAachen/drk-design-system.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.9.0","description":"DRK shared UI design system for Next.js projects","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"],"*.{scss,css,json,yml,yaml}":["prettier --write"]},"_nodeVersion":"24.14.0","dependencies":{"sanitize-html":"^2.17.0","@fontsource/merriweather":"^5.2.11"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sass":"^1.77.0","husky":"^9.1.0","jsdom":"^26.0.0","eslint":"^9.39.0","vitest":"^4.0.18","prettier":"^3.0.0","@eslint/js":"^9.0.0","typescript":"^5.6.0","@types/node":"^25.3.3","lint-staged":"^16.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","typescript-eslint":"^8.56.1","eslint-plugin-react":"^7.37.0","@types/sanitize-html":"^2.16.0","@testing-library/react":"^16.3.0","eslint-config-prettier":"^10.1.0","eslint-plugin-jsx-a11y":"^6.10.0","@testing-library/jest-dom":"^6.0.0","eslint-plugin-react-hooks":"^5.0.0","@testing-library/user-event":"^14.6.1"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/design-system-ui_1.1.1_1772796412585_0.45630936378996223","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@drkaachen/design-system-ui","version":"1.2.1","description":"DRK shared UI design system for Next.js projects","type":"module","private":false,"license":"MIT","main":"./index.ts","types":"./index.ts","exports":{".":"./index.ts","./components/Button/Button":"./components/Button/Button.tsx","./components/*":"./components/*","./components/*/*":"./components/*/*","./styles/*":"./styles/*","./lib/*":"./lib/*","./assets/*":"./assets/*"},"workspaces":["packages/*"],"repository":{"type":"git","url":"git+https://github.com/DRKAachen/drk-design-system.git"},"homepage":"https://github.com/DRKAachen/drk-design-system#readme","bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"publishConfig":{"access":"public"},"peerDependencies":{"next":">=14.0.0","react":">=18.0.0","react-dom":">=18.0.0"},"dependencies":{"@fontsource/merriweather":"^5.2.11","@fontsource/open-sans":"^5.2.7","sanitize-html":"^2.17.0"},"devDependencies":{"@eslint/js":"^9.0.0","@testing-library/jest-dom":"^6.0.0","@testing-library/react":"^16.3.0","@testing-library/user-event":"^14.6.1","@types/node":"^25.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/sanitize-html":"^2.16.0","eslint":"^9.39.0","eslint-config-prettier":"^10.1.0","eslint-plugin-jsx-a11y":"^6.10.0","eslint-plugin-react":"^7.37.0","eslint-plugin-react-hooks":"^5.0.0","husky":"^9.1.0","jsdom":"^26.0.0","lint-staged":"^16.0.0","prettier":"^3.0.0","sass":"^1.77.0","typescript":"^5.6.0","typescript-eslint":"^8.56.1","vitest":"^4.0.18"},"scripts":{"lint":"eslint .","lint:fix":"eslint . --fix","format":"prettier --write .","format:check":"prettier --check .","typecheck":"tsc --noEmit","typecheck:workspaces":"npm run typecheck --workspaces --if-present","test":"vitest run","test:workspaces":"npm run test --workspaces --if-present","prepare":"husky"},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"],"*.{scss,css,json,yml,yaml}":["prettier --write"]},"engines":{"node":">=20.0.0"},"gitHead":"1c92f5e60ef7f0251917de3da11462a2df8aab10","_id":"@drkaachen/design-system-ui@1.2.1","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-OBaaSHL7jTbXlmrCaUuDZU2mSb1Ct3vHkSf+N3cBr92Oz28r+gdiPeQQlSOUwOvpwa7dhtB8ge+B7x88t4xmxA==","shasum":"b67ebbfecddfeb77da9b13ae400f2e537ef453b7","tarball":"https://registry.npmjs.org/@drkaachen/design-system-ui/-/design-system-ui-1.2.1.tgz","fileCount":48,"unpackedSize":159700,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drkaachen%2fdesign-system-ui@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQ+5BVJgcE3qx2iiTLsESprvYiapTEf32ZSu1axSV7RAIgUgsy/zYk5TYvu0m94op0+Yy76Z3wV+G+/tqNgniCbZs="}]},"_npmUser":{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"},"directories":{},"maintainers":[{"name":"afielen","email":"axel.fielen@drk-aachen.de"},{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/design-system-ui_1.2.1_1773142096806_0.3023712967686085"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-05T12:19:01.586Z","modified":"2026-03-10T11:28:17.448Z","1.0.0":"2026-03-05T12:19:01.819Z","1.0.1":"2026-03-05T12:35:15.966Z","1.1.0":"2026-03-05T16:11:29.712Z","1.1.1":"2026-03-06T11:26:52.745Z","1.2.1":"2026-03-10T11:28:16.932Z"},"bugs":{"url":"https://github.com/DRKAachen/drk-design-system/issues"},"license":"MIT","homepage":"https://github.com/DRKAachen/drk-design-system#readme","repository":{"type":"git","url":"git+https://github.com/DRKAachen/drk-design-system.git"},"description":"DRK shared UI design system for Next.js projects","maintainers":[{"name":"afielen","email":"axel.fielen@drk-aachen.de"},{"name":"alikilicaslan","email":"ali.kilicaslan@drk-aachen.de"}],"readme":"# DRK Frontend Monorepo\n\nMonorepo for DRK frontend packages with strict separation between UI, CMS adapters, and runtime helpers.\n\n## Package Architecture\n\n### `@drkaachen/design-system-ui` (repo root)\n\nUI-only package with:\n- React components (Header, Footer, Navigation, Button, Modal, Alert, form controls, CookieBanner, Spinner, DrkLogo)\n- SCSS tokens, mixins, globals (including dark mode and reduced-motion support)\n- Cookie consent helpers with DSGVO enforcement utilities\n- HTML sanitizer, scroll-lock utility, and CSP security headers helper\n\nThis package intentionally does **not** include:\n- Sanity client\n- GROQ queries\n- Hostname middleware\n\n### `@drkaachen/content-sanity` (`packages/content-sanity`)\n\nOptional Sanity adapter package:\n- Sanity client and image URL builder (`urlFor`)\n- GROQ queries\n- `getSiteByHostname`\n\n### `@drkaachen/next-site-runtime` (`packages/next-site-runtime`)\n\nOptional Next.js runtime package:\n- Middleware for hostname-to-site header propagation\n- Runtime helpers for reading site headers\n- Proxy trust and hostname allowlist configuration\n\n## Why We Split It\n\n- Keeps app templates clean and lightweight by default\n- Avoids forced CMS dependencies in UI-only apps\n- Reduces security/privacy surface when CMS is not needed\n- Better GDPR/DSGVO risk control by enabling integrations explicitly per app\n\n## Install in Consuming Apps\n\n### 1) UI-only baseline (recommended default)\n\n```bash\nnpm install @drkaachen/design-system-ui\n```\n\n### 2) Add Sanity only if required\n\n```bash\nnpm install @drkaachen/content-sanity @drkaachen/next-site-runtime\n```\n\n## UI Package Usage (`@drkaachen/design-system-ui`)\n\nImport globals once in your root layout:\n\n```tsx\nimport '@drkaachen/design-system-ui/styles/globals.scss'\n```\n\nUse UI components from package root:\n\n```tsx\nimport {\n  Header,\n  Footer,\n  CookieBanner,\n  Button,\n  Modal,\n  Alert,\n  Input,\n  Label,\n  DrkLogo,\n  type SiteConfig,\n} from '@drkaachen/design-system-ui'\n```\n\n### DRK Logo\n\nThe `DrkLogo` component renders the official DRK circular emblem (red cross with \"DEUTSCHES ROTES KREUZ\" text around the perimeter). It is used automatically in the `Header` when no custom `logoUrl` is provided and can also be used standalone:\n\n```tsx\n<DrkLogo size={42} />\n```\n\n**Setup:** Copy `assets/drk-logo.png` from this package to your app's `public/` directory:\n\n```bash\ncp node_modules/@drkaachen/design-system-ui/assets/drk-logo.png public/drk-logo.png\n```\n\nThe `src` prop defaults to `/drk-logo.png` but can be overridden if you place the file elsewhere.\n\nUI `SiteConfig` is CMS-agnostic and expects a plain `logoUrl` string:\n\n```tsx\nconst site: SiteConfig = {\n  _id: 'site-1',\n  name: 'DRK Aachen',\n  hostname: 'example.de',\n  defaultLocale: 'de',\n  logoUrl: '/images/logo.svg',\n  navigation: [{ label: 'Startseite', href: '/' }],\n}\n```\n\n### Cookie Consent Enforcement (DSGVO)\n\nThe design system provides consent storage **and** enforcement utilities. Consuming apps must gate non-essential functionality behind consent checks:\n\n```tsx\nimport { hasConsent, onConsentChange } from '@drkaachen/design-system-ui'\n\n// Gate analytics behind user consent\nif (hasConsent('analytics')) {\n  initAnalytics()\n}\n\n// Forward consent events to a server-side audit log (DSGVO Art. 7(1))\nonConsentChange((consent) => {\n  fetch('/api/consent-log', {\n    method: 'POST',\n    body: JSON.stringify(consent),\n  })\n})\n```\n\nAvailable consent utilities:\n- `hasConsent(category)` — checks if a specific category is consented to\n- `hasAllConsent(categories)` — checks multiple categories at once\n- `clearConsentData()` — removes all consent data (Art. 17 right to erasure)\n- `getConsentDataForExport()` — returns consent in portable JSON (Art. 20 data portability)\n- `onConsentChange(callback)` — subscribe to consent changes (returns unsubscribe function)\n\n### Security Headers\n\nApply recommended security headers in consuming apps:\n\n```tsx\n// In next.config.ts\nimport { securityHeaders } from '@drkaachen/design-system-ui/lib/security-headers'\n\nconst nextConfig = {\n  async headers() {\n    return [{ source: '/(.*)', headers: securityHeaders() }]\n  },\n}\n```\n\nOr in middleware:\n\n```tsx\nimport { applySecurityHeaders } from '@drkaachen/design-system-ui/lib/security-headers'\n\nconst response = NextResponse.next()\napplySecurityHeaders(response.headers, {\n  extraImgSrc: ['https://*.sanity.io'],\n})\n```\n\n### Scroll Lock\n\niOS-safe body scroll lock for modals and drawers (used internally by Modal and Navigation):\n\n```tsx\nimport { lockBodyScroll, unlockBodyScroll } from '@drkaachen/design-system-ui'\n```\n\n## Optional Sanity Integration\n\nGet site content via `@drkaachen/content-sanity`, then map it to UI shape:\n\n```tsx\nimport { getSiteByHostname, urlFor } from '@drkaachen/content-sanity'\nimport type { SiteConfig as UiSiteConfig } from '@drkaachen/design-system-ui'\n\nexport async function getUiSite(hostname: string): Promise<UiSiteConfig | null> {\n  const cmsSite = await getSiteByHostname(hostname)\n  if (!cmsSite) return null\n\n  return {\n    _id: cmsSite._id,\n    name: cmsSite.name,\n    hostname: cmsSite.hostname,\n    defaultLocale: cmsSite.defaultLocale,\n    logoUrl: cmsSite.logo ? urlFor(cmsSite.logo).height(120).fit('max').auto('format').url() : undefined,\n    navigation: cmsSite.navigation,\n    footerLinks: cmsSite.footerLinks,\n  }\n}\n```\n\nIf middleware-based site header propagation is needed:\n\n```ts\n// middleware.ts in consuming app root\nexport { middleware, config } from '@drkaachen/next-site-runtime/middleware'\n```\n\n## Breaking Changes (Hard Cut)\n\nThe previous mixed package surface was removed by design.\n\n- Old package name `@drkaachen/design-system` -> `@drkaachen/design-system-ui`\n- Removed from UI package:\n  - `getSiteByHostname`\n  - `client`, `urlFor`, GROQ query exports\n  - `middleware` export\n  - `BlockRenderer` and `LegalPage` components\n- New optional packages:\n  - `@drkaachen/content-sanity`\n  - `@drkaachen/next-site-runtime`\n\n## Accessibility (WCAG 2.2 AA)\n\nThe design system follows WCAG 2.2 Level AA guidelines:\n\n- **Color contrast:** Headings use `$color-text-accent` which maps to `$drk-rot` (#e60005, ~4.0:1 ratio) — WCAG AA compliant for large text (>=18pt / >=14pt bold, requires 3:1). For small body text that needs red, use `$drk-rot-text` (#c20004, ~5.2:1 ratio) which meets WCAG AA for all text sizes.\n- **Reduced motion:** Global `prefers-reduced-motion: reduce` media query disables all transitions and animations. A `@mixin reduced-motion` is available for component-level use.\n- **Dark mode:** Basic `prefers-color-scheme: dark` support with dark tokens.\n- **Focus indicators:** Visible 2px red outline with white shadow ring on all interactive elements via `@mixin focus-outline`.\n- **Keyboard navigation:** Modal focus trap, Navigation arrow-key/Escape/Home/End support, skip link.\n- **Screen readers:** Proper ARIA attributes (`aria-expanded`, `aria-haspopup`, `aria-controls`, `aria-live`, `role=\"alert\"`, `role=\"status\"`).\n- **Form accessibility:** All form components support `aria-describedby` for linking to error/hint IDs from Label.\n\n## App Template Guidance\n\nFor `drk-app-template`:\n- Keep default dependency set to `@drkaachen/design-system-ui` only\n- Provide an optional documented module/feature toggle for Sanity setup\n- Only add Sanity env vars and middleware in apps that explicitly enable Sanity\n\n## Public Registry Setup\n\nPackages are published publicly to npm. Consuming apps can install without extra registry auth:\n\n```bash\nnpm install @drkaachen/design-system-ui\n```\n\nOptional internal mirror setup (if you later switch to private hosting) is documented in\n`templates/npmrc.consuming-app.private`.\n\n## Automated Updates in Consuming Apps\n\nUse Dependabot with the template:\n\n- `templates/dependabot.consuming-app.yml`\n\nIt tracks:\n- `@drkaachen/design-system-ui`\n- `@drkaachen/content-sanity`\n- `@drkaachen/next-site-runtime`\n\n## Local Development (Monorepo)\n\nInstall dependencies at repository root:\n\n```bash\nnpm install\n```\n\nRun quality checks:\n\n```bash\nnpm run lint          # ESLint (TypeScript, React, jsx-a11y)\nnpm run format:check  # Prettier formatting check\nnpm run typecheck     # TypeScript strict mode\nnpm run typecheck:workspaces\nnpm run test          # Vitest (128 tests across 8 files)\n```\n\nRun showcase:\n\n```bash\ncd showcase\nnpm run dev\n```\n\n### Pre-commit Hooks\n\nHusky + lint-staged runs automatically on `git commit`:\n- `*.{ts,tsx}` → ESLint fix + Prettier\n- `*.{scss,css,json,yml,yaml}` → Prettier\n\n## Publishing\n\nPublishing runs via GitHub Actions (`.github/workflows/publish.yml`) and pushes to npm. The publish job requires the `npm-publish` environment (configure required reviewers in GitHub repo settings).\n\nRequirements before first release:\n\n- Create an npm organization/user that owns the `@drkaachen` scope.\n- Add `NPM_TOKEN` as a repository secret (token with publish rights for that scope).\n- Create a GitHub environment named `npm-publish` with required reviewers.\n- Bump package versions intentionally before triggering release.\n\nThen trigger the publish workflow manually or via GitHub Release.\n\n## Fonts and GDPR/DSGVO\n\nAll fonts are self-hosted via `@fontsource` packages — no external CDN requests at runtime.\n\n| Font | Purpose | Weights | Source |\n|------|---------|---------|--------|\n| Open Sans | Body text, UI elements | 300, 400, 500, 700 | `@fontsource/open-sans` |\n| Merriweather | Headings | 300, 400, 700 | `@fontsource/merriweather` |\n\nThe body font stack follows the [DRK Styleguide](https://styleguide.drk.de/deutsches-rotes-kreuz/digital/webseite):\n\n```\n'Helvetica Neue', 'Open Sans', Helvetica, Arial, sans-serif\n```\n\n- **macOS/iOS users** get Helvetica Neue (pre-installed system font)\n- **All other users** get Open Sans (self-hosted, free, closest visual match to Helvetica Neue per DRK styleguide)\n- Helvetica Neue is a commercial font (Monotype). It is referenced as a system font only — no licensed font files are bundled.\n- No per-project font file copy is required.\n- Do not load fonts from external CDNs.\n\n### Cookie Inventory (for Datenschutzerklärung)\n\n| Cookie/Storage | Name | Purpose | Category | Retention |\n|---------------|------|---------|----------|-----------|\n| Cookie | `drk_cookie_consent_v1` | Stores user's cookie consent preferences | Necessary (TTDSG §25(2)) | 365 days |\n| localStorage | `drk_cookie_consent` | Mirrors consent for faster client-side access | Necessary | Until cleared |\n\nConsuming apps must include this information in their Datenschutzerklärung.\n\n## Security\n\nSee `SECURITY.md` for vulnerability reporting and disclosure process.\n\nBranch protection is recommended:\n- Require CI to pass before merge\n- Require at least 1 review on pull requests\n- Prevent force-push to `main`\n","readmeFilename":"README.md"}