{"id":51317573,"url":"https://github.com/arc-language/arc-cookie-bar","last_synced_at":"2026-07-01T09:01:14.018Z","repository":{"id":362097456,"uuid":"1257190088","full_name":"arc-language/arc-cookie-bar","owner":"arc-language","description":"GDPR-compliant cookie consent bar for Arc public pages. Granular categories, localStorage persistence, zero dependencies","archived":false,"fork":false,"pushed_at":"2026-06-02T14:23:04.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T16:13:23.742Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://arc-language.dev/docs/arc-cookie-bar","language":"Arc","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arc-language.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-02T12:54:55.000Z","updated_at":"2026-06-02T14:34:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arc-language/arc-cookie-bar","commit_stats":null,"previous_names":["arc-language/arc-cookie-bar"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/arc-language/arc-cookie-bar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc-language%2Farc-cookie-bar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc-language%2Farc-cookie-bar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc-language%2Farc-cookie-bar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc-language%2Farc-cookie-bar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arc-language","download_url":"https://codeload.github.com/arc-language/arc-cookie-bar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc-language%2Farc-cookie-bar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34999792,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-01T09:01:12.997Z","updated_at":"2026-07-01T09:01:14.012Z","avatar_url":"https://github.com/arc-language.png","language":"Arc","funding_links":[],"categories":[],"sub_categories":[],"readme":"# arc-cookie-bar\n\nGDPR/CCPA-compliant cookie consent bar for Arc public pages. Three consent flows, granular per-category toggles, localStorage persistence, and an optional server-side audit log — zero dependencies, ~3.5KB inline (CSS + JS + HTML).\n\n## Features\n\n- **Three consent flows** — Accept All, Reject All, or per-category customization\n- **localStorage persistence** — survives reloads; configurable expiry (default 365 days)\n- **`arcCookieConsent` event** — fires on every page load with stored or fresh prefs\n- **`window.arcCookieBar` API** — `open()`, `getConsent()`, `reset()` for programmatic control\n- **Three positions** — `bottom` (default), `top`, `modal`\n- **Server audit log** — opt-in `POST /arc-cookie-bar/api/consent` with IP anonymization\n- **Arc theme** — inherits `--ui-*` CSS variables automatically\n- **GPU-composited animations** — `will-change: transform, opacity` on the banner\n- **Accessible** — `role=\"dialog\"`, `aria-modal`, `aria-expanded`, keyboard nav, Escape to dismiss\n- **GDPR-safe server logging** — IPv4 last octet zeroed, IPv6 truncated to 64 bits\n- **Zero dependencies** — no npm installs, no build step\n\n## Install\n\n```bash\nnpm install @arc-lang/arc-cookie-bar\n```\n\nAdd to `arc.config.json`:\n\n```json\n{\n  \"packages\": [\"@arc-lang/arc-cookie-bar\"]\n}\n```\n\n## Quick start\n\n```arc\nimport CookieBar from \"@arc-cookie-bar/widgets/CookieBar.arc\"\n\npage \"Home\"\n  CookieBar(policyUrl=\"/privacy\")\n  main\n    h1 \"Welcome\"\n```\n\nThe bar appears on first visit, stores consent for 365 days, and fires `arcCookieConsent` on every subsequent load so your scripts can conditionally initialise trackers.\n\n## Widget props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `position` | String | `\"bottom\"` | `\"bottom\"` \\| `\"top\"` \\| `\"modal\"` |\n| `categories` | String | `\"necessary,analytics,marketing\"` | Comma-separated consent categories |\n| `policyUrl` | String | `\"/privacy\"` | Link to your cookie/privacy policy |\n| `expires` | Int | `365` | Days before consent expires and bar re-appears |\n| `title` | String | `\"We use cookies\"` | Banner headline |\n| `description` | String | `\"…\"` | Banner body text |\n| `serverPersist` | Bool | `false` | POST each decision to `/arc-cookie-bar/api/consent` |\n\n## Reacting to consent\n\n`arcCookieConsent` fires on every page load — immediately with stored prefs if consent already exists, or after the user acts on the banner. Wire it before any tracker initialisation:\n\n```html\n\u003cscript\u003e\nwindow.addEventListener('arcCookieConsent', function(e) {\n  if (e.detail.analytics) initGA4()\n  if (e.detail.marketing) initMetaPixel()\n})\n\u003c/script\u003e\n```\n\n`e.detail` is a flat object of booleans, one per category:\n\n```json\n{ \"necessary\": true, \"analytics\": true, \"marketing\": false, \"_e\": 1748736000000 }\n```\n\n(`_e` is the expiry timestamp — ignore it in your handler.)\n\n## `window.arcCookieBar` API\n\n```javascript\nwindow.arcCookieBar.open()         // re-open the bar (e.g. from a footer \"Cookie settings\" link)\nwindow.arcCookieBar.getConsent()   // returns stored consent object, or null if not yet set / expired\nwindow.arcCookieBar.reset()        // clear localStorage and re-show the bar (useful for testing)\n```\n\nWire a footer link:\n\n```arc\nbutton onclick=\"window.arcCookieBar.open()\" \"Cookie settings\"\n```\n\n## Custom categories\n\nYou can define any categories:\n\n```arc\nCookieBar(\n  categories=\"necessary,analytics,marketing,preferences,social\"\n  policyUrl=\"/cookies\"\n)\n```\n\n`necessary` is always locked on. Built-in descriptions exist for `necessary`, `analytics`, `marketing`, and `preferences`. Any other name renders without a description.\n\n## Server-side consent logging\n\nFor GDPR audit trails, enable server persistence:\n\n```arc\nCookieBar(serverPersist=true policyUrl=\"/privacy\")\n```\n\nEach consent decision POSTs to `POST /arc-cookie-bar/api/consent`. The table is created lazily on first request — no migration needed.\n\n**Schema:**\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `id` | INTEGER | Auto-increment PK |\n| `necessary` | INTEGER | Always `1` |\n| `analytics` | INTEGER | `1` if accepted |\n| `marketing` | INTEGER | `1` if accepted |\n| `preferences` | INTEGER | `1` if accepted |\n| `ip` | TEXT | Anonymized IP — IPv4 last octet zeroed (`1.2.3.0`), IPv6 truncated to 64 bits (`2001:db8:1:2::`) |\n| `ua` | TEXT | User-agent string, max 512 chars |\n| `created_at` | TEXT | UTC timestamp |\n\n**Security:** The endpoint enforces a same-origin check: if the browser sends an `Origin` header, the request is rejected unless `Origin` matches the `Host` header (default ports 80/443 are normalised before comparison). Direct server-to-server POSTs without an `Origin` header (curl, cron jobs) are accepted — if you need to restrict those, add an Arc middleware.\n\n## Positions\n\n```arc\nCookieBar(position=\"bottom\")   // fixed to bottom (default) — slides up on show\nCookieBar(position=\"top\")      // fixed to top — slides down on show\nCookieBar(position=\"modal\")    // centered overlay with semi-transparent backdrop\n```\n\n## Theming\n\nThe bar inherits your Arc theme's CSS variables:\n\n| Variable | Used for |\n|----------|---------|\n| `--ui-bg` | Banner background |\n| `--ui-bg-2` | \"Reject all\" button background |\n| `--ui-text` | Primary text |\n| `--ui-muted` | Secondary text and category descriptions |\n| `--ui-border` | Border, divider, and toggle track |\n| `--ui-accent` | \"Accept all\" button, links, active toggle |\n| `--ui-radius` | Border radius for bar and buttons |\n| `--arc-font-sans` | Font family |\n\nOverride specific elements by targeting `.arc-cb` in your own stylesheet:\n\n```css\n.arc-cb { --ui-accent: #6366f1; }         /* custom accent */\n.arc-cb__btn--accept { font-weight: 700; } /* bolder accept button */\n```\n\n## Performance\n\n| Metric | Value |\n|--------|-------|\n| Client payload (CSS + JS + HTML) | ~3.5 KB inline |\n| Consent check on repeat visit | ~0.1 ms (single `localStorage.getItem` + `JSON.parse`) |\n| First-paint impact | None — bar starts hidden, shown after paint |\n| DOM nodes (banner) | 14 |\n| DOM nodes (customize panel, when open) | +4 per category |\n| Server INSERT | O(log n) SQLite B-tree — \u003c 0.1 ms |\n\n## GDPR compliance notes\n\n- `necessary` is always `true` and its toggle is disabled — no legal basis needed\n- All other categories default to `false` — opt-in by default\n- Consent is re-prompted after `expires` days\n- Server-side IP is anonymized before storage — IPv4 last octet zeroed, IPv6 truncated to first 64 bits\n- Consent records are write-only from the browser — no read endpoint is provided\n\n## Browser support\n\nAll evergreen browsers (Chrome 80+, Firefox 75+, Safari 14+, Edge 80+). No IE11 support — uses `CustomEvent`, `fetch`, `localStorage`, `requestAnimationFrame`, and optional chaining via `?.` in Arc-compiled server code only.\n\n## License\n\nMIT — see [LICENSE](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farc-language%2Farc-cookie-bar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farc-language%2Farc-cookie-bar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farc-language%2Farc-cookie-bar/lists"}