diff --git a/.babelrc.js b/.babelrc.js deleted file mode 100644 index 1f74718f..00000000 --- a/.babelrc.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - "plugins": [ - "@babel/plugin-transform-runtime", - "convert-to-json" - ], - "presets": [ - ["@babel/preset-env", { - "modules": process.env.BABEL_ENV === 'cjs' ? 'cjs' : false, - "targets": { - "ie": "11", - "firefox": "60" // firefox esr - } - }], - "@babel/preset-react" - ] -}; diff --git a/.gitignore b/.gitignore index d00722d8..58ad0908 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ -node_modules -/dist/orejime.js -/dist/orejime.css -/dist/orejime.scss -/es -/lib +/node_modules +/dist/*.js +/dist/*.cjs +/dist/*.mjs +/dist/*.css +/dist/*.scss +/dist/*.ts +/dist/*.cts +/dist/*.mts +/dist/stats.json +/dist/example-assets/migrations.js diff --git a/.npmignore b/.npmignore index 50e34266..0c298dc0 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,8 @@ * -!dist/orejime.js -!dist/orejime.css -!dist/orejime.scss -!es/**/* -!lib/**/* +!/dist/*.js +!/dist/*.cjs +!/dist/*.mjs +!/dist/*.d.ts +!/dist/*.css +!/dist/*.scss +!/dist/*.LICENSE.txt diff --git a/.prettierrc.json b/.prettierrc.json index adcc35f1..9313c869 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -12,5 +12,13 @@ "tabWidth": 3, "trailingComma": "none", "useTabs": true, - "parser": "babel" + "overrides": [ + { + "files": ["package.json", "package-lock.json"], + "options": { + "tabWidth": 2, + "useTabs": false + } + } + ] } diff --git a/README.md b/README.md index 71ae772f..a01b7c8a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Orejime 🍪 -> Let your users choose the cookies they eat on your website. +> Let your users choose the cookies they eat on your website. > Orejime 🍪 is an easy to use consent manager that focuses on accessibility. ## Introduction @@ -29,7 +29,7 @@ The easiest way to use the lib is to include the built files directly in the bro ``` -If you're using this method, please avoid using the `@latest` version. Prefer a fixed one like `https://unpkg.com/orejime@2.0.1/...`. +If you're using this method, please avoid using the `@latest` version. Prefer a fixed one like `https://unpkg.com/orejime@2.0.1/...`. That way you can ensure you will not be impacted by a change of API or a potential bug that could land in the latest version. #### Via npm @@ -40,7 +40,7 @@ Orejime 🍪 is a React lib. Make sure you already installed react and react-dom npm install orejime ``` -The CSS is located in `node_modules/orejime/dist/orejime.css`. Import it directly in your JS thanks to webpack, or install it any way you are used to in your project. +The CSS is located in `node_modules/orejime/dist/orejime.css`. Import it directly in your JS thanks to webpack, or install it any way you are used to in your project. You can also directly consume the Sass file if you prefer, located in the same folder. Note: if you don't have a React environment but still want to use npm in order to easily get the latest version of Orejime, the already-built JS file is located in `node_modules/orejime/dist/orejime.js`. @@ -53,14 +53,14 @@ For IE11, you'll need to have ES6 polyfills loaded on your page. One easy and ef For each third-party script you want Orejime to manage, you must modify its ` ``` @@ -73,75 +73,62 @@ For external scripts or img tags (for tracking pixels), do the same, and rename + ``` ### Configuration -You need to pass Orejime 🍪 a configuration object with, at the very least, `apps` and `privacyPolicy` properties. Each app listed in `apps` must itself have at least `name`, `title` and `cookies`. +You need to pass Orejime 🍪 a configuration object with, at the very least, `purposes` and `privacyPolicyUrl` properties. Each purpose listed in `purposes` must itself have at least `id`, `title` and `cookies`. -
+
Here is a fully-detailed annotated example of a configuration object:   ```js var orejimeConfig = { - // Optional. You can customize the ID of the
that Orejime will create when starting up. - // The generated
will be inserted at the beginning of the . - // If there is already a DOM element with this id, Orejime will use it instead of creating a new element. - // defaults to "orejime". - elementID: "orejime", - - // Optional. For accessibility's sake, the Orejime modal must know what is the element - // containing your app or website. Orejime should *not* be in this element. - // The idea is your DOM could look like this after Orejime is initialized: - // - //
...
- //
your actual website
- // - // - // It is highly recommended to set this option, even though it's not required. - // defaults to undefined. - appElement: "#app", - - // Optional. You can customize the name of the cookie that Orejime uses for storing - // user consent decisions. - // defaults to "orejime". - cookieName: "orejime", - - // Optional. You can set a custom expiration time for the Orejime cookie, in days. - // defaults to 365. - cookieExpiresAfterDays: 365, - - // Optional. You can provide a custom domain for the Orejime cookie, for example to make it available on every associated subdomains. - cookieDomain: 'mydomain.com', - - // Optional. You can provide a custom function to serialize the cookie contents. - stringifyCookie: (contents) => JSON.stringify(contents), - - // Optional. You can provide a custom function to unserialize the cookie contents. - parseCookie: (cookie) => JSON.parse(cookie), + // Optional. You can customize the element that will contain Orejime (either + // a selector or a DOM element). + // It no element matches, an element will be created and inserted at the + // beginning of the . + orejimeElement: "#orejime", + + // Optional. + cookie: { + // Optional. You can customize the name of the cookie that Orejime uses for storing + // user consent decisions. + // defaults to "orejime". + name: "orejime", + + // Optional. You can set a custom expiration time for the Orejime cookie, in days. + // defaults to 365. + duration: 365, + + // Optional. You can provide a custom domain for the Orejime cookie, for example to make it available on every associated subdomains. + domain: 'mydomain.com', + + // Optional. You can provide a custom function to serialize the cookie contents. + stringify: (contents) => JSON.stringify(contents), + + // Optional. You can provide a custom function to unserialize the cookie contents. + parse: (cookie) => JSON.parse(cookie), + }, // You must provide a link to your privacy policy page - privacyPolicy: "", - - // Optional. Applications configured below will be ON by default if default=true. - // defaults to true - default: true, + privacyPolicyUrl: "", - // Optional. If "mustConsent" is set to true, Orejime will directly display the consent - // manager modal and not allow the user to close it before having actively - // consented or declined the use of third-party apps. + // Optional. If `forceModal` is set to true, Orejime will directly display + // the consent modal and not allow the user to close it before having actively + // consented or declined the use of third-party purposes. // defaults to false - mustConsent: false, + forceModal: false, - // Optional. If "mustNotice" is set to true, Orejime will display the consent - // notice and not allow the user to close it before having actively - // consented or declined the use of third-party apps. - // Has no effect if mustConsent is set to true. + // Optional. If `forceBanner` is set to true, Orejime will display the consent + // notice and not allow the user to close it before having actively consented + // or declined the use of third-party purposes. + // Has no effect if `forceModal` is set to true. // defaults to false - mustNotice: false, + forceBanner: false, // Optional. You can define the UI language directly here. If undefined, Orejime will // use the value given in the global "lang" variable, or fallback to the value @@ -150,59 +137,44 @@ var orejimeConfig = { // Optional. You can pass an image url to show in the notice. // If the image is not exclusively decorative, you can pass an object - // with the image src and alt attributes: `logo: {src: '...', alt: '...'}` - // defaults to false - logo: false, + // with the image `src` and `alt` attributes: `logo: {src: '...', alt: '...'}` + logo: '/img/logo.png', - // Optional. Set Orejime in debug mode to have a few stuff - // logged in the console, like warning about missing translations. - // defaults to false - debug: false, + // Optional. The theme used to render the UI (See the "theming" section below). + // If unset, this will default to "orejime", the classic built-in UI. + theme: 'orejime', // You can overwrite existing translations and add translations for your - // app descriptions and purposes. See `src/translations.yml` for a full + // purpose descriptions and purposes. See `src/translations` for a full // list of translations that can be overwritten translations: { - en: { - consentModal: { - description: "This is an example of how to override an existing translation already used by Orejime", - }, - inlineTracker: { - description: "Example of an inline tracking script", - }, - externalTracker: { - description: "Example of an external tracking script", - }, - purposes: { - analytics: "Analytics", - security: "Security" - }, - categories: { - analytics: { - description: "A long form description of the category." - } - } - }, + modal: { + description: "This is an example of how to override an existing translation already used by Orejime", + } }, - // The list of third-party apps that Orejime will manage for you. - // The apps will appear in the modal in the same order as defined here. - apps: [ + // The list of third-party purposes that Orejime will manage for you. + // The purposes will appear in the modal in the same order as defined here. + purposes: [ { - // The name of the app, used internally by Orejime. - // Each name should match a name of a + + + + + + + + + + diff --git a/dist/example-assets/buttons.js b/dist/example-assets/buttons.js new file mode 100644 index 00000000..2fa009db --- /dev/null +++ b/dist/example-assets/buttons.js @@ -0,0 +1,11 @@ +document.querySelectorAll('[data-orejime-open]').forEach(function (trigger) { + trigger.addEventListener('click', function () { + window.orejime.prompt(); + }); +}); + +document.querySelectorAll('[data-orejime-reset]').forEach(function (trigger) { + trigger.addEventListener('click', function () { + window.orejime.manager.clearConsents(); + }); +}); diff --git a/dist/example-assets/logo-footer.svg b/dist/example-assets/logo-footer.svg deleted file mode 100644 index c909196f..00000000 --- a/dist/example-assets/logo-footer.svg +++ /dev/null @@ -1,125 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dist/example-assets/logo.svg b/dist/example-assets/logo.svg index 165a80cd..0636b376 100644 --- a/dist/example-assets/logo.svg +++ b/dist/example-assets/logo.svg @@ -1,49 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - + diff --git a/dist/example-assets/style.css b/dist/example-assets/style.css index 986eeebf..11ee91ad 100644 --- a/dist/example-assets/style.css +++ b/dist/example-assets/style.css @@ -1,55 +1,50 @@ :root { - --base-font: 'Barlow', -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial, sans-serif; - --headings-font: 'Barlow Condensed', -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial, sans-serif; - --gray: #A3B0BF; --gray-light: #dde2e7; - --gray-lighter: #e8ecef; - --magenta: #d6084f; - --magenta-alt: #AB0D42; - --magenta-focus: #e90956; - --magenta-active: #c30748; + --font: Ubuntu, -apple-system, BlinkMacSystemFont, avenir next, avenir, + segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, + arial, sans-serif; + --surface: #fff; + --on-surface: #092632; + --contrast-surface: #092632; + --on-contrast-surface: #fff; + --theme: #addff3; + --interactive: #d6084f; + --interactive-alt: #ab0d42; + --interactive-focus: #e90956; + --interactive-active: #c30748; --cyan: #367591; - --cyan-alt: #245C73; - --cyan-dark: #1b3b4a; - --cyan-darker: #0b191f; --cyan-lighter: #edf0f2; - --cyan-light: #abd0e0; - --white: #f3f8fa; - --positive: #b6d608; - --positive-focus: #c6e909; - --positive-active: #a6c307; - --negative: #d62808; - --negative-focus: #e92c09; - --negative-active: #c32407; + --cyan-overlay: rgba(54, 117, 145, 0.5); + --white: #fff; --unit: 1.5rem; + --spacing-y: calc(2 * var(--unit)); } -:focus { - outline: 2px solid var(--cyan); -} - -::-moz-selection { - background: var(--cyan); - color: var(--white); +@media screen and (min-height: 30rem) { + :root { + --spacing-y: calc(3 * var(--unit)); + } } -::selection { - background: var(--cyan); - color: var(--white); +@media screen and (min-height: 45rem) { + :root { + --spacing-y: calc(4 * var(--unit)); + } } html { margin: 0; padding: 0; - color: var(--cyan-alt); - line-height: 1.5; - font-size: 100%; - font-family: var(--base-font); + background: var(--surface); + color: var(--on-surface); + line-height: var(--unit); + font-size: 87.5%; + font-family: var(--font); } -@media screen and (min-width: 30rem) { +@media screen and (min-width: 45rem) { html { - font-size: 112.5%; + font-size: 100%; } } @@ -61,7 +56,6 @@ body { h1, h2 { margin: 0; - font-family: var(--headings-font); } p { @@ -73,7 +67,7 @@ p { } a { - color: var(--magenta); + color: var(--interactive); text-decoration: underline; transition: background-color 75ms ease-in; } @@ -87,84 +81,68 @@ a:active { text-decoration: none; } +.is-hidden { + display: none; +} + .Button { + display: inline-block; border: 0; border-radius: calc(var(--unit) / 4); - padding: calc(var(--unit) / 3) calc(var(--unit) / 2); + padding: calc(var(--unit) / 2) calc(0.75 * var(--unit)); + background: var(--interactive); color: var(--white); - background: var(--magenta); + text-align: center; + text-decoration: none; font: inherit; font-weight: 600; - cursor: pointer; - transition: background-color 75ms ease-in; + transition: all 75ms ease-out; } .Button:hover, .Button:focus { - background: var(--magenta-focus); + box-shadow: 0.075rem 0.1875rem #0d1f26; + background: #f91262; + transform: translate(-0.075rem, -0.1875rem); } .Button:active { - background: var(--magenta-active); -} - -.ButtonGroup .Button { - display: inline-block; - margin: 0 1px 1px 0; -} - -@media screen and (max-width: 30rem) { - .ButtonGroup { - text-align: center; - } + background: #b30443; } .Container { - margin: 0 auto; - padding-left: 1.5rem; - padding-right: 1.5rem; + box-sizing: border-box; + margin: auto; + padding-left: var(--unit); + padding-right: var(--unit); max-width: 75ch; } .Header { + padding-top: calc(var(--spacing-y) / 2); + color: rgb(38, 31, 13); + background: var(--theme) + radial-gradient( + circle at 1.1rem 1.1rem, + rgba(28, 13, 38, 0.1) 0.15rem, + rgba(0, 0, 0, 0) 0.15rem, + rgba(0, 0, 0, 0) 100% + ) + 50% 0.5rem/2.2rem 2.2rem fixed; +} + +.Header-inner { display: flex; - flex-direction: column-reverse; - align-items: flex-start; - padding-top: calc(2 * var(--unit)); -} - -.Header-title { - margin-top: calc(4 * var(--unit)); - margin-bottom: calc(3 * var(--unit)); - color: var(--cyan-dark); - text-align: center; - line-height: 1; - font-size: calc(2 * var(--unit)); -} - -@media screen and (min-width: 20rem) { - .Header-title { - font-size: calc(3 * var(--unit)); - } -} - -@media screen and (min-width: 30rem) { - .Header-title { - margin-left: calc(2 * var(--unit)); - text-align: left; - } -} - -@media screen and (min-height: 45rem) { - .Header-title { - margin-top: calc(5 * var(--unit)); - } + flex-direction: row-reverse; + align-items: center; + justify-content: flex-end; + gap: var(--unit); } -.Header-empreinteLogo { +.Header-logo { display: block; align-self: start; - height: calc(2 * var(--unit)); + height: calc(2.5 * var(--unit)); } main { @@ -173,62 +151,49 @@ main { .Hero { margin-bottom: calc(1.5 * var(--unit)); - font-size: var(--unit); -} - -.SubHero { - margin-bottom: var(--unit); - font-style: italic; -} - -.Inset { - margin-bottom: var(--unit); - border-radius: calc(0.75 * var(--unit)); - padding: var(--unit); - background: var(--gray-light); - background: linear-gradient(112.5deg, var(--gray-lighter) 0%, var(--gray-light) 100%); -} - -@media screen and (min-width: 30rem) { - .Inset { - padding: calc(1.5 * var(--unit)); - } -} - -.Inset-title { - margin-top: calc(4 * var(--unit)); + padding: var(--spacing-y) 0; + color: rgb(38, 31, 13); + background: var(--theme) + radial-gradient( + circle at 1.1rem 1.1rem, + rgba(28, 13, 38, 0.1) 0.15rem, + rgba(0, 0, 0, 0) 0.15rem, + rgba(0, 0, 0, 0) 100% + ) + 50% 0.5rem/2.2rem 2.2rem fixed; +} + +.Hero-inner { + box-shadow: 0 0 var(--unit) var(--theme); + background: var(--theme); + font-size: 1.25rem; + line-height: 1.2; +} + +.Hero-title { margin-bottom: var(--unit); - color: var(--cyan-dark); - text-align: center; line-height: 1; - font-size: var(--unit); -} - -@media screen and (min-width: 30rem) { - .Inset-title { - margin-left: calc(1.5 * var(--unit)); - text-align: left; - } -} - -@media screen and (min-height: 45rem) { - .Inset-title { - margin-top: calc(5 * var(--unit)); - } + font-size: calc(2 * var(--unit)); + line-height: 0.95; } -#privacyPolicy:target { - background: #64a7c5; +.Section-title { + margin-top: var(--spacing-y); + margin-bottom: calc(1.5 * var(--unit)); + color: var(--cyan); + line-height: 1.4; + font-size: var(--unit); + font-weight: 700; } -.Users { +.Tiles { margin: 0; padding: 0; list-style: none; } @media screen and (min-width: 20rem) { - .Users { + .Tiles { display: grid; grid-template-columns: 1fr 1fr; grid-auto-rows: 1fr; @@ -237,12 +202,12 @@ main { } @media screen and (min-width: 40rem) { - .Users { + .Tiles { grid-template-columns: 1fr 1fr 1fr; } } -.User { +.Tile { box-sizing: border-box; display: flex; justify-content: center; @@ -254,186 +219,80 @@ main { height: 100%; } -@media screen and (min-width: 20rem) { - .User { - margin-bottom: 0; +@media screen and (min-width: 55rem) { + .Tile--wide { + margin-left: calc(-1 * var(--unit)); + margin-right: calc(-1 * var(--unit)); } } -.User:hover, -.User:focus { - border-color: rgba(214, 8, 79, 0.2); -} - -.User:active { - background: rgba(214, 8, 79, 0.3); -} - -.User img { - max-width: 100%; -} - -.Footer { - padding: calc(2 * var(--unit)) 0; - background: var(--gray-light); - background: linear-gradient(112.5deg, var(--gray-lighter) 0%, var(--gray-light) 100%); -} - -.Footer p { - position: relative; - margin: 0; -} - -@media screen and (min-width: 30rem) { - .Footer p { - background: url('logo-footer.svg') no-repeat center right; +@media screen and (min-width: 70rem) { + .Tile--full { + margin-left: calc(-5 * var(--unit)); + margin-right: calc(-5 * var(--unit)); } } -.orejime-ModalOverlay, -.orejime-NoticeOverlay { - background: rgba(54, 117, 145, 0.75); -} - -.orejime-Notice, -.orejime-Modal { - background: var(--cyan-darker); - background: linear-gradient(112.5deg, var(--cyan-dark) 0%, var(--cyan-darker) 100%); - color: var(--white); -} - -@media screen and (min-width: 990px) { - .orejime-Notice { - max-width: 40ch; - } -} - -.orejime-Notice-body { - padding: calc(var(--unit) / 2); -} - -.orejime-Notice-purposes { - color: var(--cyan-light); -} - -.orejime-Notice-description { - margin-bottom: calc(var(--unit) / 2); - text-align: center; -} - -.orejime-Notice-actions { - text-align: center; -} - -.orejime-Modal a { - color: var(--white); +a.Tile:hover, +a.Tile:focus { + border-color: rgba(214, 8, 79, 0.2); } -.orejime-Modal a:hover, -.orejime-Modal a:focus { +a.Tile:active { background: rgba(214, 8, 79, 0.3); } -.orejime-Modal-header { - border: 0; - padding: calc(1.5 * var(--unit)); - padding-bottom: 0; +.Tile img { + max-width: 100%; } -.orejime-Modal-title { +.MigrationForm-input, +.MigrationForm-output { margin-bottom: var(--unit); - line-height: 1; - font-size: 2em; -} - -.orejime-Modal-body { - padding: calc(1.5 * var(--unit)); - padding-bottom: calc(var(--unit) / 2); -} - -.orejime-Modal-footer { - border: 0; - padding: calc(1.5 * var(--unit)); -} - -.orejime-Modal-title { - font-family: var(--headings-font); -} - -.orejime-AppToggles { - margin-bottom: calc(1.5 * var(--unit)); -} - -.orejime-AppToggles button { - margin: 0 1px 1px 0; -} - -.orejime-AppList-item { - padding-left: calc(50px + var(--unit)); -} - -.orejime-AppItem-purposes { - color: var(--cyan-light); -} - -.orejime-Button { - padding: calc(var(--unit) / 4) calc(var(--unit) / 2); - font-weight: 600; - cursor: pointer; - transition: background-color 75ms ease-in; -} - -.orejime-Button--info { - background: var(--magenta); -} - -.orejime-Button--info:hover, -.orejime-Button--info:focus { - background: var(--magenta-focus); -} - -.orejime-Button--info:active { - background: var(--magenta-active); + border: 1px solid currentColor; + border-radius: calc(var(--unit) / 4); + width: 100%; + padding: calc(var(--unit) / 2); + font: inherit; + font-family: monospace; } -.orejime-Button--decline { - background: var(--negative); +.MigrationForm-output { + overflow-x: auto; } -.orejime-Button--decline:hover, -.orejime-Button--decline:focus { - background: var(--negative-focus); +.Footer { + padding: calc(3 * var(--unit)) 0; + background: var(--contrast-surface); + color: var(--on-contrast-surface); } -.orejime-Button--decline:active { - background: var(--negative-active); +.Footer p { + position: relative; + margin: 0; } -.orejime-Button--save, -.orejime-Button--accept { - background: var(--positive); - color: var(--cyan-darker); +.Footer ul { + margin: 0; + padding: 0; + list-style: none; } -.orejime-Button--save:hover, -.orejime-Button--save:focus, -.orejime-Button--accept:hover, -.orejime-Button--accept:focus { - background: var(--positive-focus); -} -.orejime-Button--save:active, -.orejime-Button--accept:active { - background: var(--positive-active); +.Footer li { + display: inline; } -.orejime-AppItem-input:checked + .orejime-AppItem-label .orejime-AppItem-slider { - background: var(--magenta); +.Footer li:nth-child(n + 2):before { + content: ' • '; } -.orejime-Modal-privacyPolicyLink { - color: var(--cyan-light); +.Footer a { + color: #fff; } -.orejime-Modal-poweredByLink { - display: none; +.orejime-Env { + --orejime-color-background: #eff9fd !important; + --orejime-color-text: var(--on-surface) !important; + --orejime-color-interactive: var(--interactive) !important; + --orejime-color-shadow: 2, 10, 15 !important; } diff --git a/dist/index.html b/dist/index.html index 04a0dd98..f9efda74 100644 --- a/dist/index.html +++ b/dist/index.html @@ -2,13 +2,18 @@ - - - + + + - Orejime - Easy to use consent manager that focuses on accessibility + + Orejime - Easy to use consent manager that focuses on accessibility + - + @@ -22,107 +27,207 @@ -->
-
-
-

- Orejime -

- - - - -
- -
-
-

- Let your users choose the cookies they eat on your website.
- Orejime is an easy to use - consent manager that focuses on accessibility. -

- -

- - See the documentation on GitHub - -

+
+
+ +
+
-

- This is a demo page showing a glimpse of how Orejime works.
- You can view the source code of this page or the documentation - for more details. -

+
+
+
+
+

Orejime

+ +

+ Let your users choose the cookies they eat on your + website. +
+ Orejime + + is an easy to use consent manager that focuses on + accessibility. +

+ +

+ + See the documentation on GitHub + +

+
+
+
-

Privacy policy example

+
+

Privacy policy example

This is a sample section that acts as the privacy policy - page in this example.
+ page in this example. +
Here you could, for example, let users open the consent modal anytime with a simple button, or even reset their consent preferences.

- - + +
-

They're using Orejime

+

They're using Orejime

-
-
+
+