├── .babelrc ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── CNAME ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── content ├── downloads.md ├── faq.md ├── implementation.md ├── orgs.md ├── press.md └── testimonials.md ├── next-sitemap.js ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── .well-known │ └── gpc.json ├── CNAME ├── GPC_for_Users.pdf ├── Implementing GPC for Publishers.pdf ├── favicon.ico ├── fonts │ ├── roboto-v20-latin-700.woff │ ├── roboto-v20-latin-700.woff2 │ ├── roboto-v20-latin-700italic.woff │ ├── roboto-v20-latin-700italic.woff2 │ ├── roboto-v20-latin-900.woff │ ├── roboto-v20-latin-900.woff2 │ ├── roboto-v20-latin-900italic.woff │ ├── roboto-v20-latin-900italic.woff2 │ ├── roboto-v20-latin-italic.woff │ ├── roboto-v20-latin-italic.woff2 │ ├── roboto-v20-latin-regular.woff │ └── roboto-v20-latin-regular.woff2 ├── img │ ├── arrow-icons │ │ ├── caret-down-fill.svg │ │ └── caret-right-fill.svg │ ├── author_aram@3x.jpg │ ├── author_seb@3x.jpg │ ├── avatar.png │ ├── bonta@3x.jpg │ ├── browser-fights-for-you.svg │ ├── browser-logos │ │ ├── DDG.svg │ │ ├── abine.svg │ │ ├── brave.svg │ │ ├── disconnect.svg │ │ ├── firefox.svg │ │ ├── optmeowt.svg │ │ └── privacybadger.svg │ ├── contact-bg.svg │ ├── divider-lines.svg │ ├── download-logos │ │ ├── abine.png │ │ ├── badger.png │ │ ├── brave.png │ │ ├── disconnect.png │ │ ├── duckduckgo.png │ │ ├── firefox.png │ │ ├── lockr.png │ │ ├── mee.svg │ │ └── optmeowt.png │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-48x48.png │ │ └── favicon.ico │ ├── gpc-logo-mobile.svg │ ├── gpc-logo-small.svg │ ├── gpc-logo.svg │ ├── gpc-social-avatar.jpg │ ├── gpc-social-big.jpg │ ├── hero-bg.svg │ ├── live-more-privately.svg │ ├── participating-logos-gray │ │ ├── .complianz~imageoptim.svg │ │ ├── .onetrust~imageoptim.svg │ │ ├── abine.svg │ │ ├── automattic.svg │ │ ├── brave.svg │ │ ├── cafemedia.svg │ │ ├── complianz.svg │ │ ├── cr.svg │ │ ├── dcn.svg │ │ ├── disconnect.svg │ │ ├── duckduckgo-horizontal.svg │ │ ├── duckduckgo-vertical.svg │ │ ├── eff.svg │ │ ├── ft.svg │ │ ├── glitch.svg │ │ ├── meredith.svg │ │ ├── microsoft.svg │ │ ├── mozilla.svg │ │ ├── nsf.svg │ │ ├── nyt.svg │ │ ├── onetrust.svg │ │ ├── optmeowt.svg │ │ ├── privacybadger.svg │ │ ├── samsung.svg │ │ ├── sourcepoint.svg │ │ ├── twitter.svg │ │ ├── washington-post.svg │ │ ├── wesleyan.svg │ │ └── wirewheel.svg │ ├── participating-logos │ │ ├── .complianz~imageoptim.svg │ │ ├── .onetrust~imageoptim.svg │ │ ├── abine.svg │ │ ├── automattic.svg │ │ ├── brave.svg │ │ ├── cafemedia.svg │ │ ├── checkmyads.svg │ │ ├── complianz.svg │ │ ├── cr.svg │ │ ├── dcn.svg │ │ ├── didomi.png │ │ ├── disconnect.svg │ │ ├── duckduckgo-horizontal.svg │ │ ├── duckduckgo-vertical.svg │ │ ├── eff.svg │ │ ├── firefox.svg │ │ ├── ft.svg │ │ ├── glitch.svg │ │ ├── ketch.svg │ │ ├── meredith.svg │ │ ├── microsoft.svg │ │ ├── mozilla.svg │ │ ├── nsf.svg │ │ ├── nyt.svg │ │ ├── onetrust.svg │ │ ├── optery.svg │ │ ├── optmeowt.svg │ │ ├── privacybadger.svg │ │ ├── raptive.svg │ │ ├── samsung.svg │ │ ├── sourcepoint.svg │ │ ├── trustarc.svg │ │ ├── twitter.svg │ │ ├── washington-post.svg │ │ ├── wesleyan.svg │ │ └── wirewheel.svg │ ├── person-with-computer.svg │ ├── press-logos │ │ ├── Chicago_Trib.png │ │ ├── LA_Times.png │ │ ├── Mashable-1.png │ │ ├── Mashable.png │ │ ├── TechCrunch.png │ │ ├── The_Verge.png │ │ ├── Vox.png │ │ ├── cnet-white.svg │ │ ├── gpc-white.svg │ │ ├── techcrunch-white.svg │ │ ├── washpo-white.svg │ │ └── wired-white.svg │ ├── press-release-logos │ │ ├── brave.svg │ │ ├── cafemedia.svg │ │ ├── complianz.svg │ │ ├── duckduckgo.svg │ │ ├── meredith.svg │ │ ├── nyt.svg │ │ ├── onetrust.svg │ │ ├── sourcepoint.svg │ │ ├── washington-post.svg │ │ └── wirewheel.svg │ ├── state-of-california@3x.png │ ├── testimonial-imgs │ │ ├── becerra.jpg │ │ ├── warner.jpg │ │ ├── white.jpg │ │ └── wyden.png │ ├── turn-on-gpc.svg │ └── wifi.svg ├── site.webmanifest ├── sitemap.xml ├── vercel.svg └── washingtonpost.svg └── src ├── components ├── article.js ├── article.module.css ├── browsers-plugins.js ├── browsers-plugins.module.css ├── button.js ├── button.module.css ├── carousel.js ├── carousel.module.css ├── collapse.js ├── collapse.module.css ├── faq-list.js ├── faq-list.module.css ├── featured-articles.js ├── featured-articles.module.css ├── featured-organizations.js ├── featured-organizations.module.css ├── home.js ├── home.module.css ├── implementation.module.css ├── layout.js ├── layout.module.css ├── navbar.js ├── navbar.module.css ├── org-list.js ├── org-list.module.css ├── orgs-header.js ├── orgs-header.module.css ├── press-list.js ├── press-list.module.css ├── press-release.js ├── press-release.module.css ├── slider.js ├── slider.module.css ├── status-bar.js ├── status-bar.module.css ├── visually-hidden.js └── visually-hidden.module.css ├── pages ├── _app.js ├── _document.js ├── faq.js ├── implementation.js ├── index.js ├── orgs.js ├── orgs.module.css ├── press-release │ ├── 20201007.js │ └── 20210128.js ├── press.js └── press.module.css ├── styles ├── fonts.css ├── globals.css └── variables.css └── utils └── markdown.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [] 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 🛎️ 11 | uses: actions/checkout@v2 12 | 13 | - uses: actions/setup-node@v2.1.2 14 | with: 15 | node-version: '18.17.0' 16 | 17 | - name: Cache node modules 18 | uses: actions/cache@v4.2.0 19 | with: 20 | path: ~/.npm 21 | key: v1-${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 22 | restore-keys: | 23 | ${{ runner.os }}-node- 24 | 25 | - name: Install dependencies 26 | run: npm ci 27 | 28 | - name: Build 🔧 29 | run: | 30 | npm run build 31 | touch out/.nojekyll 32 | 33 | - name: Deploy 🚀 34 | uses: JamesIves/github-pages-deploy-action@3.7.1 35 | with: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | BRANCH: gh-pages 38 | FOLDER: out 39 | CLEAN: true 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source 2 | *.com 3 | *.class 4 | *.dll 5 | *.exe 6 | *.o 7 | *.so 8 | 9 | # Packages 10 | # it's better to unpack these files and commit the raw source 11 | # git has its own built in compression methods 12 | *.7z 13 | *.dmg 14 | *.gz 15 | *.iso 16 | *.jar 17 | *.rar 18 | *.tar 19 | *.zip 20 | 21 | # Logs and databases 22 | *.log 23 | *.sql 24 | *.sqlite 25 | 26 | # OS generated files 27 | .DS_Store 28 | .DS_Store? 29 | ._* 30 | .Spotlight-V100 31 | .Trashes 32 | ehthumbs.db 33 | Thumbs.db 34 | 35 | # Byte-compiled / optimized / DLL files 36 | __pycache__/ 37 | *.py[cod] 38 | *$py.class 39 | 40 | # C extensions 41 | *.so 42 | 43 | # Distribution / packaging 44 | .Python 45 | build/ 46 | develop-eggs/ 47 | downloads/ 48 | eggs/ 49 | .eggs/ 50 | lib/ 51 | lib64/ 52 | parts/ 53 | sdist/ 54 | var/ 55 | wheels/ 56 | pip-wheel-metadata/ 57 | share/python-wheels/ 58 | *.egg-info/ 59 | .installed.cfg 60 | *.egg 61 | MANIFEST 62 | 63 | # PyInstaller 64 | # Usually these files are written by a python script from a template 65 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 66 | *.manifest 67 | *.spec 68 | 69 | # Installer logs 70 | pip-log.txt 71 | pip-delete-this-directory.txt 72 | 73 | # Unit test / coverage reports 74 | htmlcov/ 75 | .tox/ 76 | .nox/ 77 | .coverage 78 | .coverage.* 79 | .cache 80 | nosetests.xml 81 | coverage.xml 82 | *.cover 83 | *.py,cover 84 | .hypothesis/ 85 | .pytest_cache/ 86 | 87 | # Translations 88 | *.mo 89 | *.pot 90 | 91 | # Django stuff: 92 | *.log 93 | local_settings.py 94 | db.sqlite3 95 | db.sqlite3-journal 96 | 97 | # Flask stuff: 98 | instance/ 99 | .webassets-cache 100 | 101 | # Scrapy stuff: 102 | .scrapy 103 | 104 | # Sphinx documentation 105 | docs/_build/ 106 | 107 | # PyBuilder 108 | target/ 109 | 110 | # Jupyter Notebook 111 | .ipynb_checkpoints 112 | 113 | # IPython 114 | profile_default/ 115 | ipython_config.py 116 | 117 | # pyenv 118 | .python-version 119 | 120 | # pipenv 121 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 122 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 123 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 124 | # install all needed dependencies. 125 | #Pipfile.lock 126 | 127 | # celery beat schedule file 128 | celerybeat-schedule 129 | 130 | # SageMath parsed files 131 | *.sage.py 132 | 133 | # Environments 134 | .env 135 | .venv 136 | env/ 137 | venv/ 138 | ENV/ 139 | env.bak/ 140 | venv.bak/ 141 | 142 | # Spyder project settings 143 | .spyderproject 144 | .spyproject 145 | 146 | # Rope project settings 147 | .ropeproject 148 | 149 | # mkdocs documentation 150 | /site 151 | 152 | # mypy 153 | .mypy_cache/ 154 | .dmypy.json 155 | dmypy.json 156 | 157 | # Pyre type checker 158 | .pyre/ 159 | 160 | /dist 161 | 162 | # dependencies 163 | /node_modules 164 | /.pnp 165 | .pnp.js 166 | 167 | # testing 168 | /coverage 169 | 170 | # next.js 171 | /.next/ 172 | /out/ 173 | 174 | # production 175 | /build 176 | 177 | # misc 178 | .DS_Store 179 | *.pem 180 | 181 | # debug 182 | npm-debug.log* 183 | yarn-debug.log* 184 | yarn-error.log* 185 | 186 | # local env files 187 | .env.local 188 | .env.development.local 189 | .env.test.local 190 | .env.production.local 191 | 192 | # vercel 193 | .vercel 194 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | globalprivacycontrol.org 2 | www.globalprivacycontrol.org 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | info[at]globalprivacycontrol.org. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![][gpc-logo]][gpc-url] 2 | [![License](https://img.shields.io/badge/license-CC%20BY%204.0-0c7453)](https://github.com/globalprivacycontrol/landing-page/blob/master/LICENSE) 3 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-0c7453.svg)](CODE_OF_CONDUCT.md) 4 | [![Twitter](https://img.shields.io/twitter/follow/globalprivctrl.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=globalprivctrl) 5 | 6 | # Global Privacy Control Landing Page 7 | 8 | Landing page for Global Privacy Control at https://globalprivacycontrol.org/ 9 | 10 | This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). 11 | By participating, you are expected to uphold this code. Please report unacceptable behavior via our Github repository. 12 | 13 | ## Run Landing Page on Local Server 14 | 15 | To run the landing page on a local server, clone this repo and run 16 | 17 | ```terminal 18 | npm install 19 | ``` 20 | 21 | to install required packages. Then, to build and preview the site, run 22 | 23 | ```terminal 24 | npm run build 25 | npm run dev 26 | ``` 27 | 28 | ## Getting Involved 29 | 30 | We appreciate offers to help but are not currently accepting pull requests for non-essential or design-related updates. 31 | 32 | ## License 33 | 34 | Apart from any logos or marks that may be contained in this repo, this work is licensed under a 35 | [Creative Commons Attribution 4.0 International License](https://github.com/globalprivacycontrol/landing-page/blob/master/LICENSE). 36 | 37 | [![CC BY 4.0][cc-by-image]][cc-by] 38 | 39 | ## Resources 40 | 41 | - Read [proposed specification](https://privacycg.github.io/gpc-spec/) for technical details 42 | - Visit [test site](https://global-privacy-control.glitch.me) to learn how to interact with the GPC signal 43 | 44 | [cc-by]: http://creativecommons.org/licenses/by/4.0/ 45 | [cc-by-image]: https://i.creativecommons.org/l/by/4.0/88x31.png 46 | [gpc-url]: https://globalprivacycontrol.org/ 47 | [gpc-logo]: https://pbs.twimg.com/profile_banners/1311398695162703872/1601662219/1500x500 48 | -------------------------------------------------------------------------------- /content/downloads.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Join over 50 million users. 3 | entries: 4 | - name: Abine DeleteMe 5 | url: https://joindeleteme.com/blog/how-to-enable-global-privacy-control/ 6 | img: /img/download-logos/abine.png 7 | - name: Brave Browser 8 | url: https://brave.com/web-standards-at-brave/4-global-privacy-control/ 9 | img: /img/download-logos/brave.png 10 | - name: Disconnect 11 | url: https://disconnect.me/ 12 | img: /img/download-logos/disconnect.png 13 | - name: DuckDuckGo Privacy Browser 14 | url: https://spreadprivacy.com/global-privacy-control-enabled-by-default/ 15 | img: /img/download-logos/duckduckgo.png 16 | - name: Firefox 17 | url: https://blog.mozilla.org/netpolicy/2021/10/28/implementing-global-privacy-control/ 18 | img: /img/download-logos/firefox.png 19 | - name: OptMeowt by privacy-tech-lab 20 | url: https://github.com/privacy-tech-lab/gpc-optmeowt 21 | img: /img/download-logos/optmeowt.png 22 | - name: Privacy Badger by EFF 23 | url: https://privacybadger.org 24 | img: /img/download-logos/badger.png 25 | - name: lockrMail by lockr 26 | url: https://lockrMail.com 27 | img: /img/download-logos/lockr.png 28 | --- 29 | 30 | Download a supported browser or extension and start exercising your privacy rights with GPC. 31 | -------------------------------------------------------------------------------- /content/orgs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Founding Organizations 3 | headerText: Global Privacy Control has broad industry support. Below, you’ll find browsers and extensions which send the GPC signal, businesses that honor GPC and other supporting partners involved in creating the specification. 4 | entries: 5 | - name: Abine DeleteMe 6 | url: https://joindeleteme.com/blog/how-to-enable-global-privacy-control/ 7 | img: /img/participating-logos/abine.svg 8 | browser_img: /img/browser-logos/abine.svg 9 | type: Downloadable 10 | isFeaturedIndex: 1 11 | inOrganizations: true 12 | - name: Automattic 13 | url: https://automattic.com/ 14 | img: /img/participating-logos/automattic.svg 15 | type: Business 16 | isFeaturedIndex: 1 17 | - name: Brave Software 18 | url: https://brave.com/global-privacy-content/ 19 | img: /img/participating-logos/brave.svg 20 | browser_img: /img/browser-logos/brave.svg 21 | type: Downloadable 22 | isFeaturedIndex: 1 23 | inOrganizations: true 24 | - name: Check My Ads 25 | url: https://checkmyads.org/ 26 | img: /img/participating-logos/checkmyads.svg 27 | type: Business 28 | - name: Complianz 29 | url: https://complianz.io/ 30 | img: /img/participating-logos/complianz.svg 31 | type: Business 32 | - name: Consumer Reports 33 | url: https://advocacy.consumerreports.org/issue/tech-privacy/ 34 | img: /img/participating-logos/cr.svg 35 | type: Business 36 | isFeaturedIndex: 1 37 | - name: Didomi 38 | url: https://www.didomi.io/ 39 | img: /img/participating-logos/didomi.png 40 | type: Business 41 | - name: Disconnect 42 | url: https://disconnect.me/ 43 | img: /img/participating-logos/disconnect.svg 44 | browser_img: /img/browser-logos/disconnect.svg 45 | type: Downloadable 46 | isFeaturedIndex: 1 47 | inOrganizations: true 48 | - name: Digital Content Next 49 | url: https://digitalcontentnext.org/ 50 | img: /img/participating-logos/dcn.svg 51 | type: Business 52 | - name: DuckDuckGo 53 | url: https://spreadprivacy.com/global-privacy-control-enabled-by-default/ 54 | img: /img/participating-logos/duckduckgo-vertical.svg 55 | browser_img: /img/browser-logos/DDG.svg 56 | type: Downloadable 57 | isFeaturedIndex: 1 58 | inOrganizations: true 59 | - name: EFF 60 | url: https://www.eff.org/ 61 | img: /img/participating-logos/eff.svg 62 | type: Business 63 | isFeaturedIndex: 1 64 | - name: Financial Times 65 | url: https://www.ft.com/ 66 | img: /img/participating-logos/ft.svg 67 | type: Business 68 | - name: Glitch 69 | url: https://glitch.com/ 70 | img: /img/participating-logos/glitch.svg 71 | type: Business 72 | - name: Firefox 73 | url: https://blog.mozilla.org/netpolicy/2021/10/28/implementing-global-privacy-control/ 74 | img: /img/participating-logos/firefox.svg 75 | browser_img: /img/browser-logos/firefox.svg 76 | type: Downloadable 77 | - name: Ketch 78 | url: https://www.ketch.com/platform/consent-management 79 | img: /img/participating-logos/ketch.svg 80 | type: Business 81 | - name: lockr 82 | url: https://lockrmail.com/ 83 | img: /img/download-logos/lockr.png 84 | type: Business 85 | - name: Mee Foundation 86 | url: https://mee.foundation/ 87 | img: /img/download-logos/mee.svg 88 | type: Business 89 | - name: Meredith Digital 90 | url: https://www.meredith.com/ 91 | img: /img/participating-logos/meredith.svg 92 | type: Business 93 | isFeaturedIndex: 11 94 | - name: Mozilla 95 | url: https://mozilla.org/ 96 | img: /img/participating-logos/mozilla.svg 97 | type: Business 98 | isFeaturedIndex: 1 99 | - name: National Science Foundation 100 | url: https://www.nsf.gov/ 101 | img: /img/participating-logos/nsf.svg 102 | type: Business 103 | - name: New York Times 104 | url: https://www.nytimes.com/ 105 | img: /img/participating-logos/nyt.svg 106 | type: Business 107 | isFeaturedIndex: 9 108 | - name: OneTrust 109 | url: https://www.onetrust.com/solutions/consent-management-platform/ 110 | img: /img/participating-logos/onetrust.svg 111 | type: Business 112 | - name: Optery 113 | url: https://www.optery.com/the-optery-global-privacy-control-gpc-extension/ 114 | img: /img/participating-logos/optery.svg 115 | type: Business 116 | - name: OptMeowt by privacy-tech-lab 117 | url: https://github.com/privacy-tech-lab/gpc-optmeowt 118 | img: /img/participating-logos/optmeowt.svg 119 | browser_img: /img/browser-logos/optmeowt.svg 120 | type: Downloadable 121 | - name: Privacy Badger by EFF 122 | url: https://privacybadger.org/ 123 | img: /img/participating-logos/privacybadger.svg 124 | browser_img: /img/browser-logos/privacybadger.svg 125 | type: Downloadable 126 | - name: privacy-tech-lab 127 | img: /img/participating-logos/optmeowt.svg 128 | url: https://privacytechlab.org/ 129 | type: Business 130 | - name: Raptive 131 | url: https://raptive.com/ 132 | img: /img/participating-logos/raptive.svg 133 | type: Business 134 | isFeaturedIndex: 12 135 | - name: SourcePoint 136 | url: https://www.sourcepoint.com/ 137 | img: /img/participating-logos/sourcepoint.svg 138 | type: Business 139 | - name: TrustArc 140 | url: https://trustarc.com/resource/global-privacy-control/ 141 | img: /img/participating-logos/trustarc.svg 142 | type: Business 143 | - name: Wesleyan University 144 | url: https://wesleyan.edu/ 145 | img: /img/participating-logos/wesleyan.svg 146 | type: Business 147 | - name: Washington Post 148 | url: https://www.washingtonpost.com/ 149 | img: /img/participating-logos/washington-post.svg 150 | type: Business 151 | isFeaturedIndex: 10 152 | - name: WireWheel 153 | url: https://www.wirewheel.io 154 | img: /img/participating-logos/wirewheel.svg 155 | type: Business 156 | --- 157 | 158 | The following organizations, representing 50 million users and hundreds of thousands of websites, are in support of GPC. 159 | 160 | ---Browsers 161 | green: Browsers + Extensions 162 | gray: / (Collectively Over ~50million Users) 163 | 164 | --- 165 | 166 | ---Business 167 | green: Organizations 168 | gray: / (Browser and extension vendors, publishers, consent management platforms, ...) 169 | 170 | --- 171 | 172 | ---Partners 173 | green: Supporting Partners 174 | 175 | --- 176 | -------------------------------------------------------------------------------- /content/press.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Press & Announcements 3 | entries: 4 | - datePublished: Jan. 27, 2025 5 | contentUrl: https://www.adexchanger.com/data-privacy-roundup/if-youre-a-publisher-and-you-dont-know-what-a-uoom-is-then-read-this/ 6 | title: "If You’re A Publisher And You Don’t Know What A UOOM Is, Then Read This" 7 | source: AdExchanger 8 | - datePublished: Mar 23, 2023 9 | contentUrl: https://www.badcredit.org/news/global-privacy-control-stops-companies-from-selling-online-information/ 10 | title: "Global Privacy Control Boosts Online Privacy by Empowering Consumers with Proactive Protection" 11 | source: badcredit.org 12 | - datePublished: Oct. 26, 2021 13 | contentUrl: https://www.washingtonpost.com/technology/2021/10/26/global-privacy-control-firefox/ 14 | title: "Your browser can tell websites how to treat your data. But companies didn’t have to listen — until now" 15 | source: The Washington Post 16 | isFeaturedIndex: 2 17 | img: "/img/press-logos/washpo-white.svg" 18 | - datePublished: Jan. 28, 2021 19 | contentUrl: https://globalprivacycontrol.org/press-release/20210128 20 | title: "GPC Privacy Browser Signal Now Used by Millions and Honored By Major Publishers" 21 | source: Global Privacy Control 22 | isFeaturedIndex: 1 23 | img: "/img/press-logos/gpc-white.svg" 24 | - datePublished: Oct 8th, 2020 25 | contentUrl: https://www.theregister.com/2020/10/10/global_privacy_control/ 26 | title: Global Privacy Control emerges as latest attempt to let netizens choose whether they want to be tracked online 27 | source: The Register 28 | - datePublished: Oct 8th, 2020 29 | contentUrl: https://www.fastcompany.com/90561555/global-privacy-control-duckduckgo-eff-mozilla 30 | title: DuckDuckGo, EFF, and others just launched privacy settings for the whole internet 31 | source: Fast Company 32 | - datePublished: Oct 7th, 2020 33 | contentUrl: https://iapp.org/news/a/global-privacy-control-initiative-hopes-to-help-consumers-exercise-ccpa-rights/ 34 | title: Global Privacy Control initiative aims to help consumers exercise privacy rights 35 | source: IAPP 36 | - datePublished: Oct 7th, 2020 37 | contentUrl: https://arstechnica.com/tech-policy/2020/10/coming-to-a-browser-near-you-a-new-way-to-keep-sites-from-selling-your-data/ 38 | title: Now you can enforce your privacy rights with a single browser tick 39 | source: Ars Technica 40 | - datePublished: Oct 7th, 2020 41 | contentUrl: https://www.cnet.com/news/privacy-effort-could-stop-some-annoying-website-popups-and-online-tracking/ 42 | title: Privacy push could stop some annoying website pop-ups and online tracking 43 | source: CNET 44 | - datePublished: Oct 7th, 2020 45 | contentUrl: https://duo.com/decipher/global-privacy-control-protocol-aims-to-pick-up-where-do-not-track-left-off 46 | title: Global Privacy Control Protocol Aims to Pick Up Where Do Not Track Left Off 47 | source: Decipher 48 | - datePublished: Oct 7th, 2020 49 | contentUrl: https://techcrunch.com/2020/10/07/tech-publisher-coalition-backs-new-push-for-browser-level-privacy-controls/ 50 | title: Tech-publisher coalition backs new push for browser-level privacy controls 51 | source: TechCrunch 52 | isFeaturedIndex: 4 53 | img: "/img/press-logos/techcrunch-white.svg" 54 | - datePublished: Oct 7th, 2020 55 | contentUrl: https://www.wired.com/story/global-privacy-control-launches-do-not-track-is-back/ 56 | title: ‘Do Not Track’ Is Back, and This Time It Might Work 57 | source: Wired 58 | isFeaturedIndex: 3 59 | img: "/img/press-logos/wired-white.svg" 60 | - datePublished: Oct 7th, 2020 61 | contentUrl: https://globalprivacycontrol.org/press-release/20201007 62 | title: "Announcing Global Privacy Control: Making it Easy for Consumers to Exercise Their Privacy Rights" 63 | source: Global Privacy Control 64 | img: "/img/press-logos/gpc-white.svg" 65 | --- 66 | -------------------------------------------------------------------------------- /content/testimonials.md: -------------------------------------------------------------------------------- 1 | --- 2 | entries: 3 | - name: Xavier Becerra 4 | position: CA Attorney General 5 | quote: CA DOJ is encouraged to see the technology community developing a global privacy control in furtherance of the CCPA and consumer privacy rights. 6 | img: /img/testimonial-imgs/becerra.jpg 7 | imgAlt: Picture of Xavier Becerra 8 | url: https://twitter.com/AGBecerra/status/1313884873413726208 9 | - name: Ron Wyden 10 | position: Senate Finance Chairman 11 | quote: 40 million consumers are now using web browsers and other privacy tools that support this global opt out. Major publishers, the New York Times, Washington Post, have already pledged to respect it. California's Attorney General has already said that companies must respect GPC. This is a big step in Americans privacy, a big, big step forward. 12 | img: /img/testimonial-imgs/wyden.png 13 | imgAlt: Picture of Ron Wyden 14 | url: https://twitter.com/jason_kint/status/1369313187317878788?s=21 15 | - name: Mark R. Warner 16 | position: VA Senator 17 | quote: My hope is that Governor Northam and the legislature will improve [the newly passed Virginia Consumer Data Protection Act] in the near future in important ways... making it easier for Virginia citizens to invoke their privacy rights, such as through a global privacy control. 18 | img: /img/testimonial-imgs/warner.jpg 19 | imgAlt: Picture of Mark R. Warner 20 | url: https://www.warner.senate.gov/public/index.cfm/pressreleases?id=69CDF4A0-C7E6-4357-A0F1-E9F107FCEF8C 21 | - name: Alexander McD White 22 | position: Bermuda Privacy Commissioner 23 | quote: GPC provides a clear and binary indication of an individual's choice... Based on a review of several of the web browsers' intentions regarding GPC, it appears likely to be a prominent, easily understandable, and accessible mechanism in the browser settings. 24 | img: /img/testimonial-imgs/white.jpg 25 | imgAlt: Picture of Alexander McD White 26 | url: https://www.privacy.bm/post/global-privacy-control-interoperability-in-action 27 | - name: Ron Wyden 28 | position: Senate Finance Chairman 29 | quote: It's past time to give consumers a real and enforceable way to stop companies from tracking and selling their data. My Mind Your Own Business Act would do just that, and this project [Global Privacy Control] shows it’s possible. 30 | img: /img/testimonial-imgs/wyden.png 31 | imgAlt: Picture of Ron Wyden 32 | url: https://twitter.com/RonWyden/status/1314234870747422720 33 | - name: Xavier Becerra 34 | position: CA Attorney General 35 | quote: CCPA requires businesses to treat a user-enabled global privacy control as a legally valid consumer request to opt out of the sale of their data. CCPA opened the door to developing a technical standard, like the GPC, which satisfies this legal requirement & protects privacy. 36 | img: /img/testimonial-imgs/becerra.jpg 37 | imgAlt: Picture of Xavier Becerra 38 | url: https://twitter.com/AGBecerra/status/1354850758236102656 39 | --- 40 | 41 | This is HTML 42 | -------------------------------------------------------------------------------- /next-sitemap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteUrl: 'https://globalprivacycontrol.org', 3 | generateRobotsTxt: false 4 | }; 5 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const isProd = process.env.NODE_ENV === 'production'; 2 | 3 | const GH_PAGES_PREFIX = ''; // '/gpc-next' without CNAME 4 | 5 | module.exports = { 6 | env: { 7 | publicPrefix: isProd ? GH_PAGES_PREFIX : '', 8 | siteUrl: 'https://globalprivacycontrol.org' 9 | }, 10 | crossOrigin: 'anonymous', 11 | basePath: isProd ? GH_PAGES_PREFIX : '', 12 | assetPrefix: isProd ? GH_PAGES_PREFIX : '', 13 | output: 'export' 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "landing-page", 3 | "version": "1.0.0", 4 | "private": true, 5 | "repository": "git@github.com:globalprivacycontrol/landing-page.git", 6 | "license": "CC BY 4.0", 7 | "scripts": { 8 | "dev": "next dev", 9 | "build": "next build", 10 | "postbuild": "next-sitemap", 11 | "start": "next start", 12 | "lint": "next lint", 13 | "prettier": "prettier --ignore-path .gitignore \"**/*.+(js|json)\"", 14 | "format": "npm run prettier -- --write", 15 | "check-format": "npm run prettier -- --list-different" 16 | }, 17 | "prettier": { 18 | "singleQuote": true, 19 | "trailingComma": "none" 20 | }, 21 | "eslintConfig": { 22 | "env": { 23 | "browser": true, 24 | "es6": true, 25 | "node": true 26 | }, 27 | "extends": [ 28 | "eslint:recommended", 29 | "next", 30 | "prettier" 31 | ], 32 | "rules": { 33 | "@next/next/no-img-element": "off", 34 | "no-console": [ 35 | "warn", 36 | { 37 | "allow": [ 38 | "error", 39 | "warn", 40 | "trace" 41 | ] 42 | } 43 | ], 44 | "no-unused-vars": [ 45 | "error", 46 | { 47 | "args": "none", 48 | "ignoreRestSiblings": true 49 | } 50 | ] 51 | } 52 | }, 53 | "dependencies": { 54 | "bootstrap": "^4.5.3", 55 | "classnames": "^2.3.1", 56 | "gray-matter": "^4.0.3", 57 | "highlight.js": "^11.6.0", 58 | "js-yaml": "^4.0.0", 59 | "next": "^14.2.21", 60 | "next-sitemap": "^2.5.28", 61 | "postcss": "^8.4.31", 62 | "postcss-flexbugs-fixes": "^5.0.2", 63 | "postcss-preset-env": "^7.4.1", 64 | "postcss-url": "^10.1.3", 65 | "react": "^18.2.0", 66 | "react-dom": "^18.2.0", 67 | "remark": "^14.0.2", 68 | "remark-html": "^15.0.1", 69 | "sass": "^1.49.9" 70 | }, 71 | "devDependencies": { 72 | "@babel/eslint-parser": "^7.17.0", 73 | "babel-eslint": "^10.1.0", 74 | "eslint": "^8.10.0", 75 | "eslint-config-next": "^12.1.0", 76 | "eslint-config-prettier": "^8.4.0", 77 | "prettier": "^2.5.1", 78 | "prop-types": "^15.8.1" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const isProd = process.env.NODE_ENV === 'production'; 2 | 3 | const GH_PAGES_PREFIX = ''; // '/gpc-next' without CNAME 4 | 5 | module.exports = { 6 | plugins: [ 7 | 'postcss-flexbugs-fixes', 8 | [ 9 | 'postcss-preset-env', 10 | { 11 | autoprefixer: { 12 | flexbox: 'no-2009' 13 | }, 14 | stage: 3, 15 | importFrom: ['./src/styles/variables.css'], 16 | features: { 17 | 'custom-properties': true, 18 | 'custom-media-queries': true, 19 | 'nesting-rules': true 20 | } 21 | } 22 | ], 23 | [ 24 | 'postcss-url', 25 | { 26 | // note `rebase` option doesn't work so we use `url` 27 | url: function ({ url }) { 28 | return isProd ? `${GH_PAGES_PREFIX}${url}` : url; 29 | } 30 | } 31 | ] 32 | ] 33 | }; 34 | -------------------------------------------------------------------------------- /public/.well-known/gpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "gpc": true, 3 | "lastUpdate": "2021-05-13" 4 | } 5 | -------------------------------------------------------------------------------- /public/CNAME: -------------------------------------------------------------------------------- 1 | globalprivacycontrol.org 2 | www.globalprivacycontrol.org 3 | -------------------------------------------------------------------------------- /public/GPC_for_Users.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/GPC_for_Users.pdf -------------------------------------------------------------------------------- /public/Implementing GPC for Publishers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/Implementing GPC for Publishers.pdf -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-700.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-700.woff2 -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-700italic.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-700italic.woff2 -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-900.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-900.woff2 -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-900italic.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-900italic.woff2 -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-italic.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-italic.woff2 -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-regular.woff -------------------------------------------------------------------------------- /public/fonts/roboto-v20-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/fonts/roboto-v20-latin-regular.woff2 -------------------------------------------------------------------------------- /public/img/arrow-icons/caret-down-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/img/arrow-icons/caret-right-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/img/author_aram@3x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/author_aram@3x.jpg -------------------------------------------------------------------------------- /public/img/author_seb@3x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/author_seb@3x.jpg -------------------------------------------------------------------------------- /public/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/avatar.png -------------------------------------------------------------------------------- /public/img/bonta@3x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/bonta@3x.jpg -------------------------------------------------------------------------------- /public/img/browser-fights-for-you.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/browser-logos/disconnect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | disconnect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/img/browser-logos/optmeowt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | optmeowt 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/img/contact-bg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/divider-lines.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/download-logos/abine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/abine.png -------------------------------------------------------------------------------- /public/img/download-logos/badger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/badger.png -------------------------------------------------------------------------------- /public/img/download-logos/brave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/brave.png -------------------------------------------------------------------------------- /public/img/download-logos/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/disconnect.png -------------------------------------------------------------------------------- /public/img/download-logos/duckduckgo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/duckduckgo.png -------------------------------------------------------------------------------- /public/img/download-logos/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/firefox.png -------------------------------------------------------------------------------- /public/img/download-logos/lockr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/lockr.png -------------------------------------------------------------------------------- /public/img/download-logos/mee.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/img/download-logos/optmeowt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/download-logos/optmeowt.png -------------------------------------------------------------------------------- /public/img/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/favicon-48x48.png -------------------------------------------------------------------------------- /public/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/favicon/favicon.ico -------------------------------------------------------------------------------- /public/img/gpc-logo-mobile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/gpc-social-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/gpc-social-avatar.jpg -------------------------------------------------------------------------------- /public/img/gpc-social-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/gpc-social-big.jpg -------------------------------------------------------------------------------- /public/img/hero-bg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/disconnect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | disconnect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/eff.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | eff 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/ft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/microsoft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/mozilla.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | mozilla 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/optmeowt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/samsung.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos-gray/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos/checkmyads.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/img/participating-logos/didomi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/participating-logos/didomi.png -------------------------------------------------------------------------------- /public/img/participating-logos/disconnect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | disconnect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/img/participating-logos/eff.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | eff 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/img/participating-logos/ft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos/ketch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/participating-logos/microsoft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos/mozilla.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | mozilla 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/participating-logos/optery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/participating-logos/optmeowt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos/raptive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/img/participating-logos/samsung.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/participating-logos/trustarc.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/participating-logos/trustarc.svg -------------------------------------------------------------------------------- /public/img/participating-logos/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/press-logos/Chicago_Trib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/Chicago_Trib.png -------------------------------------------------------------------------------- /public/img/press-logos/LA_Times.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/LA_Times.png -------------------------------------------------------------------------------- /public/img/press-logos/Mashable-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/Mashable-1.png -------------------------------------------------------------------------------- /public/img/press-logos/Mashable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/Mashable.png -------------------------------------------------------------------------------- /public/img/press-logos/TechCrunch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/TechCrunch.png -------------------------------------------------------------------------------- /public/img/press-logos/The_Verge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/The_Verge.png -------------------------------------------------------------------------------- /public/img/press-logos/Vox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/press-logos/Vox.png -------------------------------------------------------------------------------- /public/img/press-logos/cnet-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/press-logos/wired-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/state-of-california@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/state-of-california@3x.png -------------------------------------------------------------------------------- /public/img/testimonial-imgs/becerra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/testimonial-imgs/becerra.jpg -------------------------------------------------------------------------------- /public/img/testimonial-imgs/warner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/testimonial-imgs/warner.jpg -------------------------------------------------------------------------------- /public/img/testimonial-imgs/white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/testimonial-imgs/white.jpg -------------------------------------------------------------------------------- /public/img/testimonial-imgs/wyden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globalprivacycontrol/landing-page/32550d8afe64baabd9da8dc1d55f4aa3c0697c4a/public/img/testimonial-imgs/wyden.png -------------------------------------------------------------------------------- /public/img/turn-on-gpc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Global Privacy Control", 3 | "icons": [ 4 | { 5 | "src": "/static/img/favicon/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/static/img/favicon/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } 19 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://globalprivacycontrol.orgdaily0.72024-10-08T21:15:43.803Z 4 | https://globalprivacycontrol.org/faqdaily0.72024-10-08T21:15:43.803Z 5 | https://globalprivacycontrol.org/implementationdaily0.72024-10-08T21:15:43.803Z 6 | https://globalprivacycontrol.org/orgsdaily0.72024-10-08T21:15:43.803Z 7 | https://globalprivacycontrol.org/pressdaily0.72024-10-08T21:15:43.803Z 8 | https://globalprivacycontrol.org/press-release/20201007daily0.72024-10-08T21:15:43.803Z 9 | https://globalprivacycontrol.org/press-release/20210128daily0.72024-10-08T21:15:43.803Z 10 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/article.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './article.module.css'; 3 | 4 | export default function Article({ title, children, ...others }) { 5 | return ( 6 |
7 |
8 |
9 |
10 |
11 |
12 |

{title}

13 |
14 |
15 |
16 | 17 |
{children}
18 |
19 |
20 |
21 | ); 22 | } 23 | 24 | Article.propTypes = { 25 | title: PropTypes.string.isRequired, 26 | children: PropTypes.any 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/article.module.css: -------------------------------------------------------------------------------- 1 | .articleWrapper { 2 | background-color: #e5e5e5; 3 | } 4 | 5 | .article { 6 | color: #49504e; 7 | padding: calc(90 * var(--px-in-rem)) 0; 8 | background-color: white; 9 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); 10 | 11 | & h1 { 12 | font-size: calc(43 * var(--px-in-rem)); 13 | font-weight: 900; 14 | line-height: calc(58 * var(--px-in-rem)); 15 | letter-spacing: -0.01em; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/browsers-plugins.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './browsers-plugins.module.css'; 3 | 4 | export default function BrowsersPlugins({ entries }) { 5 | const browsersPlugins = entries.filter((e) => e.type == 'Downloadable'); 6 | // .sort((a, b) => { 7 | // return a.isFeaturedIndex - b.isFeaturedIndex; 8 | // }); 9 | 10 | return ( 11 | 28 | ); 29 | } 30 | 31 | BrowsersPlugins.propTypes = { 32 | entries: PropTypes.arrayOf( 33 | PropTypes.shape({ 34 | name: PropTypes.string.isRequired, 35 | url: PropTypes.string.isRequired, 36 | img: PropTypes.string, 37 | browser_img: PropTypes.string, 38 | type: PropTypes.string.isRequired 39 | }) 40 | ).isRequired 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/browsers-plugins.module.css: -------------------------------------------------------------------------------- 1 | .list { 2 | display: flex; 3 | list-style: none; 4 | margin: 0; 5 | padding: 0; 6 | width: 100%; 7 | flex-wrap: wrap; 8 | 9 | & li { 10 | box-sizing: border-box; 11 | border-radius: 3px; 12 | /*filter: drop-shadow(0px 2px 8px rgba(0, 0, 0, 0.15));*/ 13 | box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, 0.15); 14 | border: 1px solid rgba(0, 0, 0, 0.15); 15 | @media(--media-breakpoint-up-lg) { 16 | max-width: 22%; 17 | } 18 | 19 | &:hover { 20 | border: 1px solid var(--text-bright-green); 21 | } 22 | margin-bottom: 24px; 23 | & + li { 24 | margin-left: 24px; 25 | @media(--media-breakpoint-down-md) { 26 | &:nth-child(2n + 1) { 27 | margin-left: 0; 28 | } 29 | } 30 | @media(--media-breakpoint-up-lg) { 31 | &:nth-child(4n + 1) { 32 | margin-left: 0; 33 | } 34 | 35 | } 36 | @media(--media-breakpoint-down-sm) { 37 | margin-left: 0; 38 | 39 | } 40 | } 41 | flex: 1 1 21%; 42 | @media(--media-breakpoint-down-md) { 43 | flex: 0 1 auto; 44 | width: calc((100% - 24px) / 2); /* 50 % */ 45 | } 46 | @media(--media-breakpoint-down-sm) { 47 | width: 100%; 48 | min-height: 10em; 49 | } 50 | } 51 | 52 | & a { 53 | border-radius: 3px; 54 | overflow: hidden; 55 | text-decoration: none; 56 | text-align: center; 57 | min-height: 100%; 58 | display: flex; 59 | flex-direction: column; 60 | @media(--media-breakpoint-down-md) { 61 | flex-direction: row; 62 | } 63 | 64 | /* image */ 65 | & > span:first-child { 66 | background-color: white; 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | min-height: 150px; 71 | @media(--media-breakpoint-down-md) { 72 | min-height: 180px; 73 | min-width: 65%; 74 | } 75 | @media(--media-breakpoint-down-sm) { 76 | min-height: auto; 77 | min-width: 100%; 78 | } 79 | & img { 80 | min-height: 3em; 81 | max-width: 100%; 82 | padding: 0 1em; 83 | 84 | /* @media(--media-breakpoint-down-sm) { 85 | padding: 0 2em; 86 | 87 | } */ 88 | } 89 | } 90 | 91 | /* title + source */ 92 | & > span:last-child { 93 | background-color: var(--bg-light); 94 | padding: 10px; 95 | display: flex; 96 | flex-direction: column; 97 | font-size: calc(14 * var(--px-in-rem)); 98 | line-height: calc(20/14); 99 | font-weight: 500; 100 | color: var(--text-dark); 101 | @media(--media-breakpoint-down-md) { 102 | justify-content: center; 103 | width: 10em; 104 | } 105 | @media(--media-breakpoint-down-sm) { 106 | width: 0px; 107 | padding: 0px; 108 | } 109 | 110 | 111 | /* source */ 112 | & span:last-child { 113 | margin-top: 1em; 114 | font-size: calc(10 * var(--px-in-rem)); 115 | line-height: 1; 116 | font-weight: 900; 117 | text-transform: uppercase; 118 | letter-spacing: 0.2em; 119 | color: white; 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/components/button.js: -------------------------------------------------------------------------------- 1 | import { forwardRef } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | import styles from './button.module.css'; 5 | 6 | const Button = forwardRef(function Button( 7 | { as: Comp = 'button', children, className, variant, ...others }, 8 | ref 9 | ) { 10 | return ( 11 | 23 | {children} 24 | 25 | ); 26 | }); 27 | 28 | Button.propTypes = { 29 | as: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), 30 | children: PropTypes.any, 31 | className: PropTypes.string, 32 | variant: PropTypes.oneOf([ 33 | 'primary', 34 | 'primaryInverted', 35 | 'secondary', 36 | 'secondaryInverted' 37 | ]) 38 | }; 39 | 40 | export default Button; 41 | -------------------------------------------------------------------------------- /src/components/button.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | font-size: calc(15 * var(--px-in-rem)); 3 | padding: calc(19 * var(--px-in-rem)); 4 | border: 1px solid transparent; 5 | border-radius: calc(74 * var(--px-in-rem)); 6 | letter-spacing: 0.1em; 7 | font-weight: 900; 8 | text-decoration: none; 9 | text-align: center; 10 | 11 | &:hover { 12 | text-decoration: none; 13 | } 14 | 15 | &:focus { 16 | outline: 0; 17 | box-shadow: 0 0 0 0.2rem var(--focus-outline); 18 | } 19 | } 20 | 21 | .primary { 22 | background-color: var(--primary-bg); 23 | color: white; 24 | text-transform: uppercase; 25 | 26 | &:hover { 27 | background-color: var(--primary-bg-hover); 28 | color: white; 29 | } 30 | } 31 | 32 | .primaryInverted { 33 | color: #1a7958; 34 | background-color: white; 35 | &:hover { 36 | color: #1a7958; 37 | background-color: rgba(255, 255, 255, 0.7); 38 | } 39 | } 40 | 41 | .secondary { 42 | border-color: var(--primary-bg); 43 | color: var(--primary-bg); 44 | background-color: transparent; 45 | text-transform: uppercase; 46 | 47 | &:hover { 48 | background: var(--primary-bg-hover); 49 | color: white; 50 | } 51 | } 52 | 53 | .secondaryInverted { 54 | border-color: white; 55 | color: white; 56 | backdrop-filter: blur(10px); 57 | 58 | &:hover { 59 | color: white; 60 | border-color: white; 61 | background: rgba(255, 255, 255, 0.3); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/carousel.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Slider, { Slide } from './slider'; 4 | import styles from './carousel.module.css'; 5 | import VisuallyHidden from './visually-hidden'; 6 | 7 | export default function Carousel({ initialStep = 0, items }) { 8 | const [step, setStep] = useState(initialStep); 9 | 10 | return ( 11 |
12 | 13 | {items.map((item) => ( 14 | 15 |
16 |
17 |

“{item.quote}”

18 |
19 | 20 |
21 | 22 | {item.imgAlt} 27 |

28 | {item.name} 29 |

30 |

31 | {item.position} 32 |

33 |
34 |
35 |
36 |
37 | ))} 38 |
39 | 40 | {/* Carousel controls */} 41 |
42 | 54 |
55 |
56 | ); 57 | } 58 | 59 | Carousel.propTypes = { 60 | initialStep: PropTypes.number, 61 | items: PropTypes.arrayOf( 62 | PropTypes.shape({ 63 | name: PropTypes.string.isRequired, 64 | position: PropTypes.string.isRequired, 65 | quote: PropTypes.string.isRequired, 66 | url: PropTypes.string.isRequired, 67 | img: PropTypes.string.isRequired, 68 | imgAlt: PropTypes.string 69 | }) 70 | ).isRequired 71 | }; 72 | -------------------------------------------------------------------------------- /src/components/carousel.module.css: -------------------------------------------------------------------------------- 1 | .carousel { 2 | & figure { 3 | text-align: center; 4 | } 5 | 6 | & a { 7 | color: var(--text-dark); 8 | } 9 | & img { 10 | width: 10rem; 11 | } 12 | } 13 | 14 | .controls { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | 19 | & ul { 20 | display: flex; 21 | list-style: none; 22 | margin: 0; 23 | padding: 0; 24 | & li + li { 25 | margin-left: 10px; 26 | } 27 | 28 | & button { 29 | line-height: 0; 30 | color: inherit; 31 | border: none; 32 | padding: 0; 33 | font: inherit; 34 | cursor: pointer; 35 | outline: inherit; 36 | width: 20px; 37 | height: 20px; 38 | background: white; 39 | border: 2px solid var(--bg-light); 40 | border-radius: 50%; 41 | &.active, 42 | &:hover { 43 | background: var(--bg-light); 44 | } 45 | } 46 | } 47 | } 48 | 49 | .sectionTitle { 50 | font-weight: 900; 51 | font-size: calc(30 * var(--px-in-rem)); 52 | line-height: calc(36 * var(--px-in-rem)); 53 | letter-spacing: -0.01em; 54 | @media (--media-breakpoint-down-sm) { 55 | font-size: calc(20 * var(--px-in-rem)); 56 | line-height: calc(26 * var(--px-in-rem)); 57 | } 58 | } 59 | 60 | .sectionText { 61 | font-size: calc(18 * var(--px-in-rem)); 62 | line-height: calc(28 * var(--px-in-rem)); 63 | margin-bottom: calc(97 * var(--px-in-rem)); 64 | 65 | & a { 66 | color: var(--text-dark); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/components/collapse.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from './collapse.module.css'; 4 | 5 | export default function Collapse({ 6 | isOpened, 7 | children, 8 | keepChildren = true, 9 | ...others 10 | }) { 11 | const ref = useRef(null); 12 | const [height, setHeight] = useState(isOpened ? 'auto' : '0px'); 13 | const [isClosing, setIsClosing] = useState(false); 14 | 15 | const prevIsOpenedRef = useRef(null); 16 | useEffect(() => { 17 | prevIsOpenedRef.current = isOpened; 18 | }); 19 | 20 | const useIsomorphicLayoutEffect = 21 | typeof window !== 'undefined' ? useLayoutEffect : useEffect; 22 | 23 | useIsomorphicLayoutEffect(() => { 24 | if (isOpened) { 25 | // opening case (transitionning from close) 26 | const $el = ref.current; 27 | 28 | if ($el && prevIsOpenedRef.current !== null) { 29 | // transition from 0px to the height of the element inner content 30 | // (the element height is 0px when closed) 31 | // This is because CSS transition on height doesn't work with `auto` 32 | const height = $el.scrollHeight; 33 | setHeight(`${height}px`); 34 | 35 | // in case when user open when it's closing we need to cancel the 36 | // isClosing transition 37 | if (isClosing) { 38 | setIsClosing(false); 39 | } 40 | 41 | const handleTransitionEnd = (e) => { 42 | // remove "height" from the element's inline styles, so it can return to its initial value 43 | setHeight('auto'); 44 | }; 45 | $el.addEventListener('transitionend', handleTransitionEnd); 46 | 47 | return () => { 48 | $el.removeEventListener('transitionend', handleTransitionEnd); 49 | }; 50 | } 51 | } else { 52 | // closing case (transition from open or from isClosing) 53 | // we need to track isClosing so that we keep the children arround during 54 | // the transition time 55 | const $el = ref.current; 56 | if ($el && (prevIsOpenedRef.current === true || isClosing)) { 57 | if (prevIsOpenedRef.current === true) { 58 | // we prepare the transition by setting the height of the element to 59 | // the height of its inner content 60 | const height = $el.scrollHeight; 61 | 62 | const transition = $el.style.transition; 63 | $el.style.transition = ''; 64 | setHeight(`${height}px`); 65 | 66 | const rafId = requestAnimationFrame(() => { 67 | $el.style.transition = transition; 68 | setIsClosing(true); 69 | }); 70 | 71 | return () => { 72 | cancelAnimationFrame(rafId); 73 | }; 74 | } else if (isClosing) { 75 | // the closing transition 76 | const handleTransitionEnd = (e) => { 77 | setIsClosing(false); 78 | }; 79 | $el.addEventListener('transitionend', handleTransitionEnd); 80 | 81 | // We need to set height to 0 _after_ the browser had time to register 82 | // the previous setHeight (so on the next animation frame) 83 | const rafId = requestAnimationFrame(() => { 84 | setHeight('0px'); 85 | }); 86 | 87 | return () => { 88 | $el.removeEventListener('transitionend', handleTransitionEnd); 89 | cancelAnimationFrame(rafId); 90 | }; 91 | } 92 | } 93 | } 94 | }, [isOpened, isClosing]); 95 | 96 | return ( 97 |
98 | {keepChildren || 99 | isOpened || 100 | isClosing || 101 | (prevIsOpenedRef.current && !isOpened && !isClosing) 102 | ? children 103 | : null} 104 |
105 | ); 106 | } 107 | 108 | Collapse.propTypes = { 109 | children: PropTypes.any, 110 | isOpened: PropTypes.bool.isRequired, 111 | keepChildren: PropTypes.bool 112 | }; 113 | -------------------------------------------------------------------------------- /src/components/collapse.module.css: -------------------------------------------------------------------------------- 1 | .collapse { 2 | overflow: hidden; 3 | height: auto; 4 | transition: height 0.3s ease-out; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/faq-list.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import classNames from 'classnames'; 3 | import { useState, useEffect } from 'react'; 4 | import Collapse from './collapse'; 5 | import styles from './faq-list.module.css'; 6 | 7 | export default function FaqList({ 8 | sections, 9 | hx, 10 | isDark = false, 11 | nRendered, 12 | rdfa 13 | }) { 14 | const rendered = nRendered ? sections.slice(0, nRendered) : sections; 15 | 16 | const [openedIndex, setOpenedIndex] = useState(null); 17 | 18 | useEffect(() => { 19 | setOpenedIndex( 20 | Math.max( 21 | rendered.findIndex((r) => r.key === window.location.hash.substring(1)), 22 | 0 23 | ) 24 | ); 25 | }, [rendered]); 26 | 27 | return ( 28 |
32 | {rendered.map(({ key, data: { title }, html }, i) => ( 33 | 43 | ))} 44 |
45 | ); 46 | } 47 | 48 | FaqList.propTypes = { 49 | sections: PropTypes.arrayOf( 50 | PropTypes.shape({ 51 | key: PropTypes.string.isRequired, 52 | data: PropTypes.shape({ 53 | title: PropTypes.string.isRequired 54 | }), 55 | html: PropTypes.string.isRequired 56 | }) 57 | ).isRequired, 58 | hx: PropTypes.oneOf(['h2', 'h3']), 59 | isDark: PropTypes.bool, 60 | nRendered: PropTypes.number, 61 | rdfa: PropTypes.bool 62 | }; 63 | 64 | function FaqSection({ 65 | id, 66 | title, 67 | html, 68 | isDark, 69 | isOpened: defaultIsOpened = false, 70 | hx: Hx = 'h2', 71 | rdfa 72 | }) { 73 | const [isOpened, setIsOpened] = useState(defaultIsOpened); 74 | 75 | return ( 76 |
80 |
81 |
87 | { 91 | e.preventDefault(); 92 | setIsOpened(!isOpened); 93 | }} 94 | > 95 | {title} 96 | 97 | 104 | 105 | 106 | 107 |
108 | 113 |
118 | 119 |
120 |
121 | ); 122 | } 123 | 124 | FaqSection.propTypes = { 125 | isOpened: PropTypes.bool, 126 | id: PropTypes.string.isRequired, 127 | title: PropTypes.string.isRequired, 128 | html: PropTypes.string.isRequired, 129 | isDark: PropTypes.bool, 130 | hx: PropTypes.oneOf(['h2', 'h3']), 131 | rdfa: PropTypes.bool 132 | }; 133 | -------------------------------------------------------------------------------- /src/components/faq-list.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | & h2, & h3{ 3 | font-size: 1rem; 4 | font-weight: 700; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | background-color: #F8F8F8; 9 | &.dark { 10 | background-color: white; 11 | } 12 | 13 | cursor: pointer; 14 | width: 100%; 15 | border: 1px solid #D8D8D8; 16 | text-align: left; 17 | outline: none; 18 | border-radius: 3px; 19 | 20 | & a { 21 | padding-top: 18px; 22 | padding-bottom: 18px; 23 | display: flex; 24 | align-items: center; 25 | color: var(--text-dark); 26 | & svg { 27 | min-width: 1rem; 28 | margin-left: auto; 29 | transition: transform 0.3s ease-out; 30 | } 31 | &:hover, &.active { 32 | text-decoration: none; 33 | color: var(--text-bright-green); 34 | & svg { 35 | fill: var(--text-bright-green); 36 | } 37 | } 38 | &.active svg { 39 | transform: rotate(90deg); 40 | } 41 | } 42 | } 43 | 44 | .content { 45 | color: var(--text-dark); 46 | line-height: calc(30 * var(--px-in-rem)); 47 | 48 | & code { 49 | font-weight: 700; 50 | } 51 | 52 | & pre { 53 | line-height: calc(20 * var(--px-in-rem)); 54 | & code { 55 | font-weight: 400; 56 | } 57 | } 58 | 59 | & blockquote { 60 | font-style: italic; 61 | margin-left: 3rem; 62 | 63 | @media (--media-breakpoint-down-sm) { 64 | margin-left: 1rem; 65 | } 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/components/featured-articles.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './featured-articles.module.css'; 3 | 4 | export default function FeaturedArticles({ entries }) { 5 | const featured = entries 6 | .filter((e) => e.isFeaturedIndex != null) 7 | .slice(0, 4) 8 | .sort((a, b) => { 9 | return a.isFeaturedIndex - b.isFeaturedIndex; 10 | }); 11 | 12 | return ( 13 | 31 | ); 32 | } 33 | 34 | FeaturedArticles.propTypes = { 35 | entries: PropTypes.arrayOf( 36 | PropTypes.shape({ 37 | datePublished: PropTypes.string.isRequired, 38 | contentUrl: PropTypes.string.isRequired, 39 | title: PropTypes.string.isRequired, 40 | source: PropTypes.string.isRequired, 41 | isFeaturedIndex: PropTypes.number, 42 | img: PropTypes.string 43 | }) 44 | ).isRequired 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/featured-articles.module.css: -------------------------------------------------------------------------------- 1 | .list { 2 | display: flex; 3 | list-style: none; 4 | margin: 0; 5 | padding: 0; 6 | width: 100%; 7 | flex-wrap: wrap; 8 | 9 | & li { 10 | background-color: white; 11 | box-sizing: border-box; 12 | border-radius: 3px; 13 | /*filter: drop-shadow(0px 2px 8px rgba(0, 0, 0, 0.15));*/ 14 | box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, 0.15); 15 | border: 1px solid rgba(0, 0, 0, 0.15); 16 | &:hover { 17 | border: 1px solid var(--text-bright-green); 18 | } 19 | margin-bottom: 24px; 20 | & + li { 21 | margin-left: 24px; 22 | @media (--media-breakpoint-down-md) { 23 | &:nth-child(2n + 1) { 24 | margin-left: 0; 25 | } 26 | } 27 | @media (--media-breakpoint-down-sm) { 28 | margin-left: 0; 29 | } 30 | } 31 | flex: 1 1 0; 32 | @media (--media-breakpoint-down-md) { 33 | flex: 0 1 auto; 34 | width: calc((100% - 24px) / 2); /* 50 % */ 35 | } 36 | @media (--media-breakpoint-down-sm) { 37 | width: 100%; 38 | min-height: auto; 39 | } 40 | } 41 | 42 | & a { 43 | border-radius: 3px; 44 | overflow: hidden; 45 | text-decoration: none; 46 | text-align: center; 47 | min-height: 100%; 48 | display: flex; 49 | flex-direction: column; 50 | @media (--media-breakpoint-down-md) { 51 | flex-direction: row; 52 | } 53 | 54 | /* image */ 55 | & > span:first-child { 56 | background-color: var(--primary-bg); 57 | display: flex; 58 | justify-content: center; 59 | align-items: center; 60 | min-height: 135px; 61 | @media (--media-breakpoint-down-md) { 62 | min-height: 180px; 63 | min-width: 33%; 64 | } 65 | @media (--media-breakpoint-down-sm) { 66 | min-height: auto; 67 | } 68 | & img { 69 | max-width: 100%; 70 | padding: 0 1em; 71 | } 72 | } 73 | 74 | /* title + source */ 75 | & > span:last-child { 76 | background-color: white; 77 | padding: 20px; 78 | display: flex; 79 | flex-direction: column; 80 | font-size: calc(14 * var(--px-in-rem)); 81 | line-height: calc(20 / 14); 82 | font-weight: 500; 83 | color: var(--text-dark); 84 | @media (--media-breakpoint-down-md) { 85 | justify-content: center; 86 | } 87 | 88 | /* source */ 89 | & span:last-child { 90 | margin-top: 2em; 91 | font-size: calc(10 * var(--px-in-rem)); 92 | line-height: 1; 93 | font-weight: 900; 94 | text-transform: uppercase; 95 | letter-spacing: 0.2em; 96 | color: var(--text-bright-green); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/components/featured-organizations.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './featured-organizations.module.css'; 3 | 4 | export default function FeaturedOrganizations({ entries }) { 5 | const featured = entries 6 | .filter((e) => e.isFeaturedIndex != null) 7 | .sort((a, b) => { 8 | return a.isFeaturedIndex - b.isFeaturedIndex; 9 | }); 10 | 11 | return ( 12 |
13 | {featured.map(({ url, img, name }) => ( 14 |
15 | 16 | {`${name} 20 | 21 |
22 | ))} 23 |
24 | ); 25 | } 26 | 27 | FeaturedOrganizations.propTypes = { 28 | entries: PropTypes.arrayOf( 29 | PropTypes.shape({ 30 | name: PropTypes.string.isRequired, 31 | url: PropTypes.string.isRequired, 32 | img: PropTypes.string, 33 | browser_img: PropTypes.string, 34 | type: PropTypes.string.isRequired, 35 | isFeaturedIndex: PropTypes.number 36 | }) 37 | ).isRequired 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/featured-organizations.module.css: -------------------------------------------------------------------------------- 1 | .orgLogo { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 100%; 6 | width: 100%; 7 | border: 1px solid transparent; 8 | padding: calc(20 * var(--px-in-rem)); 9 | min-height: calc(80 * var(--px-in-rem)); 10 | 11 | @media (--media-breakpoint-up-lg) { 12 | min-height: calc(100 * var(--px-in-rem)); 13 | } 14 | 15 | &:hover { 16 | border: 1px solid var(--text-bright-green); 17 | box-sizing: border-box; 18 | border-radius: 4px; 19 | } 20 | 21 | & img { 22 | width: 100%; 23 | height: auto; 24 | max-width: 100%; 25 | max-height: 7.5rem; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/layout.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useRouter } from 'next/router'; 3 | import Head from 'next/head'; 4 | import styles from './layout.module.css'; 5 | import Navbar from './navbar'; 6 | import StatusBar from './status-bar'; 7 | 8 | export default function Layout({ 9 | title = 'Global Privacy Control — Take Control Of Your Privacy', 10 | description = 'Exercise your privacy rights in one step via the “Global Privacy Control” (GPC) signal, a proposed specification backed by over a dozen organizations.', 11 | children, 12 | header = true 13 | }) { 14 | const router = useRouter(); 15 | 16 | return ( 17 |
18 | 19 | {/* */} 20 | {title} 21 | 22 | 23 | 27 | 28 | {/* */} 29 | 30 | 34 | 35 | 36 | 40 | 41 | {/* */} 42 | 43 | 47 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | {/* */} 61 | 64 | 65 | {header && ( 66 |
67 |
68 |
69 |
70 | 71 |
72 |
73 |
74 |
75 | )} 76 | 77 |
{children}
78 | 79 | {/* */} 80 |
81 |
82 |
83 |
84 |

Get Involved

85 | 86 |

87 | Contact us to learn more about supporting{' '} 88 | GPC in your browser, 89 | app, or website. 90 |

91 |

92 | 97 | @globalprivctrl 98 | 99 |

100 |
101 |
102 |
103 |
104 | 105 | {/* */} 106 | 131 |
132 | ); 133 | } 134 | 135 | Layout.propTypes = { 136 | title: PropTypes.string, 137 | description: PropTypes.string, 138 | children: PropTypes.any, 139 | header: PropTypes.bool 140 | }; 141 | -------------------------------------------------------------------------------- /src/components/layout.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | background-image: url("/img/contact-bg.svg"); 3 | background-size: cover; 4 | } 5 | 6 | .navWrapper { 7 | padding-bottom: calc(30 * var(--px-in-rem)); 8 | } 9 | 10 | .contact { 11 | padding: calc(154 * var(--px-in-rem)) 0 calc(130 * var(--px-in-rem)); 12 | color: white; 13 | 14 | background-image: url("/img/contact-bg.svg"); 15 | background-size: cover; 16 | 17 | & h1 { 18 | font-weight: 900; 19 | font-size: calc(30 * var(--px-in-rem)); 20 | line-height: calc(36 * var(--px-in-rem)); 21 | } 22 | 23 | & p { 24 | font-size: calc(18 * var(--px-in-rem)); 25 | line-height: calc(28 * var(--px-in-rem)) 26 | } 27 | 28 | & a { 29 | font-size: calc(20 * var(--px-in-rem)); 30 | line-height: calc(36 / 20); 31 | margin-top: calc(46 * var(--px-in-rem)); 32 | font-weight: 900; 33 | color: white; 34 | 35 | @media(--media-breakpoint-up-md) { 36 | font-size: calc(30 * var(--px-in-rem)); 37 | &[href*="twitter.com"] { 38 | font-size: calc(26 * var(--px-in-rem)); 39 | } 40 | } 41 | 42 | &:hover { 43 | color: white; 44 | } 45 | } 46 | } 47 | 48 | .footer { 49 | background: var(--bg-dark); 50 | padding: calc(42 * var(--px-in-rem)) 0 calc(42 * var(--px-in-rem)); 51 | 52 | & p { 53 | font-size: calc(12 * var(--px-in-rem)); 54 | line-height: calc(18 * var(--px-in-rem)); 55 | color: rgba(255, 255, 255, 0.5); 56 | } 57 | 58 | & a { 59 | color: white; 60 | font-weight: bold; 61 | 62 | &:hover { 63 | color: white; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/navbar.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Link from 'next/link'; 3 | import styles from './navbar.module.css'; 4 | import VisuallyHidden from './visually-hidden'; 5 | 6 | export default function Navbar({ isLarge = false }) { 7 | const [isOpen, setIsOpen] = useState(false); 8 | 9 | return ( 10 | 77 | ); 78 | } 79 | 80 | function Links() { 81 | return ( 82 | 124 | ); 125 | } 126 | -------------------------------------------------------------------------------- /src/components/navbar.module.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | padding: calc(30 * var(--px-in-rem)) 0 0; 3 | } 4 | 5 | .headerLogoLarge { 6 | width: 330px; 7 | } 8 | 9 | .headerLogoSmall { 10 | width: 120px; 11 | } 12 | 13 | .mobileMenu { 14 | transition: all 300ms ease-out; 15 | max-height: 0; 16 | &.open { 17 | max-height: 330px; 18 | } 19 | } 20 | 21 | .linkList { 22 | & a { 23 | font-size: calc(12 * var(--px-in-rem)); 24 | font-weight: 900; 25 | line-height: calc(28 * var(--px-in-rem)); 26 | color: white; 27 | padding: 0.1rem 0.75rem!important; 28 | 29 | &:hover { 30 | color: white; 31 | text-decoration: underline; 32 | } 33 | 34 | @media(--media-breakpoint-down-lg) { 35 | padding: 0.1rem 0.5rem!important; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/org-list.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './org-list.module.css'; 3 | 4 | export default function OrgList({ entries, type }) { 5 | var isBusiness = false; 6 | if (type == 'Business') { 7 | isBusiness = true; 8 | } 9 | return ( 10 | 22 | ); 23 | } 24 | 25 | OrgList.propTypes = { 26 | entries: PropTypes.arrayOf( 27 | PropTypes.shape({ 28 | name: PropTypes.string.isRequired, 29 | url: PropTypes.string.isRequired, 30 | img: PropTypes.string, 31 | browser_img: PropTypes.string, 32 | type: PropTypes.string.isRequired 33 | }) 34 | ).isRequired 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/org-list.module.css: -------------------------------------------------------------------------------- 1 | .downloadTable { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: stretch; 5 | width: 100%; 6 | list-style-type: none; 7 | padding-inline-start: 0; 8 | margin: 0; 9 | 10 | & li { 11 | display: flex; 12 | width: 100%; 13 | border: 1px solid #d8d8d8; 14 | border-bottom: none; 15 | &:last-child { 16 | border-bottom: 1px solid #d8d8d8; 17 | } 18 | 19 | &:nth-child(2n + 1) { 20 | background-color: #f8f8f8; 21 | } 22 | 23 | 24 | &:hover { 25 | cursor: pointer; 26 | /* background-color: white; */ 27 | & a { 28 | text-decoration: underline; 29 | } 30 | } 31 | } 32 | 33 | & div { 34 | display: flex; 35 | align-items: center; 36 | padding: calc(18 * var(--px-in-rem)) calc(20 * var(--px-in-rem)); 37 | 38 | & a { 39 | color: var(--text-bright-green); 40 | font-weight: 700; 41 | } 42 | } 43 | 44 | @media (--media-breakpoint-down-xs) { 45 | & div { 46 | padding: calculateRem(10px); 47 | } 48 | } 49 | } 50 | 51 | 52 | .tableDesc { 53 | flex: 1 1 0px; 54 | font-size: calc(16 * var(--px-in-rem)); 55 | line-height: calc(28 * var(--px-in-rem)); 56 | 57 | &, 58 | & > * { 59 | white-space: nowrap; 60 | overflow: hidden; 61 | text-overflow: ellipsis; 62 | } 63 | } 64 | 65 | .tableLink { 66 | font-weight: 900; 67 | font-size: calc(13 * var(--px-in-rem)); 68 | line-height: 1; 69 | text-align: right; 70 | letter-spacing: 0.2em; 71 | white-space: nowrap; 72 | 73 | @media (--media-breakpoint-only-lg) { 74 | flex: 0; 75 | } 76 | @media (--media-breakpoint-down-sm) { 77 | width: 0px; 78 | overflow: hidden; 79 | padding: 0 !important; 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/components/orgs-header.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './orgs-header.module.css'; 3 | 4 | export default function OrgsHeader({ section }) { 5 | return ( 6 |
7 |
8 |

9 | {section.green} 10 | {section.gray} 11 |

12 |
13 |
14 | ); 15 | } 16 | 17 | OrgsHeader.propTypes = { 18 | section: PropTypes.shape({ 19 | green: PropTypes.string.isRequired, 20 | gray: PropTypes.string 21 | }) 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/orgs-header.module.css: -------------------------------------------------------------------------------- 1 | .sectionTitle { 2 | & h2 { 3 | font-weight: 900; 4 | font-size: calc(12 * var(--px-in-rem)); 5 | line-height: calc(28 * var(--px-in-rem)); 6 | letter-spacing: 0.2em; 7 | color: var(--text-bright-green); 8 | text-transform: uppercase; 9 | } 10 | & span { 11 | color: var(--text-light); 12 | font-weight: 900; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/press-list.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './press-list.module.css'; 3 | 4 | export default function PressList({ entries }) { 5 | return ( 6 | 19 | ); 20 | } 21 | 22 | PressList.propTypes = { 23 | entries: PropTypes.arrayOf( 24 | PropTypes.shape({ 25 | datePublished: PropTypes.string.isRequired, 26 | contentUrl: PropTypes.string.isRequired, 27 | title: PropTypes.string.isRequired, 28 | source: PropTypes.string.isRequired, 29 | isFeaturedIndex: PropTypes.number, 30 | img: PropTypes.string 31 | }) 32 | ).isRequired 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/press-list.module.css: -------------------------------------------------------------------------------- 1 | .list { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: stretch; 5 | width: 100%; 6 | list-style-type: none; 7 | padding-inline-start: 0; 8 | margin: 0; 9 | 10 | & li { 11 | display: flex; 12 | width: 100%; 13 | border-bottom: 1px solid #d8d8d8; 14 | &:hover { 15 | cursor: pointer; 16 | background-color: white; 17 | } 18 | } 19 | 20 | & div { 21 | display: flex; 22 | align-items: center; 23 | padding: calc(18 * var(--px-in-rem)) calc(20 * var(--px-in-rem)); 24 | 25 | & a { 26 | color: var(--text-bright-green); 27 | font-weight: 700; 28 | } 29 | } 30 | 31 | @media (--media-breakpoint-down-xs) { 32 | & div { 33 | padding: calc(10 * var(--px-in-rem)); 34 | } 35 | } 36 | 37 | & a { 38 | font-weight: 700; 39 | letter-spacing: 0.01em; 40 | } 41 | 42 | } 43 | 44 | .left { 45 | color: #49504e; 46 | text-align: right; 47 | padding-left: 0 !important; 48 | padding-right: 0; 49 | @media (--media-breakpoint-down-sm) { 50 | width: 0px; 51 | overflow: hidden; 52 | padding: 0 !important; 53 | } 54 | } 55 | 56 | .desc { 57 | padding-left: 0; 58 | flex: 1 1 0px; 59 | font-size: cacl(16 * var(--px-in-rem)); 60 | line-height: calc(28 * var(--px-in-rem)); 61 | 62 | &, 63 | & > * { 64 | white-space: nowrap; 65 | overflow: hidden; 66 | text-overflow: ellipsis; 67 | } 68 | } 69 | 70 | .date { 71 | color: #49504e; 72 | text-align: right; 73 | padding-right: 0; 74 | 75 | @media (--media-breakpoint-down-sm) { 76 | width: 0px; 77 | overflow: hidden; 78 | padding: 0 !important; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/components/press-release.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import styles from './press-release.module.css'; 3 | 4 | export default function PressRelease({ children }) { 5 | return
{children}
; 6 | } 7 | 8 | PressRelease.propTypes = { 9 | children: PropTypes.any 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/press-release.module.css: -------------------------------------------------------------------------------- 1 | .pressRelease { 2 | & h1 { 3 | text-align: left; 4 | } 5 | 6 | & p { 7 | font-size: calc(18 * var(--px-in-rem)); 8 | line-height: calc(28 / 18); 9 | & a { 10 | color: inherit; 11 | font-weight: 700; 12 | text-decoration: underline; 13 | } 14 | } 15 | 16 | 17 | & blockquote { 18 | position: relative; 19 | padding: 0 1em; 20 | color: #6a737d; 21 | border-left: 0.25em solid #dfe2e5; 22 | 23 | & img { 24 | padding: 1.5rem 0; 25 | display: block; 26 | height: 50px; 27 | box-sizing: content-box; 28 | width: 100%; 29 | margin-left: -1.25rem; 30 | margin-right: -1.25rem; 31 | border-left: 0.25em solid white; 32 | } 33 | 34 | @supports (hanging-punctuation: first) { 35 | text-indent: 0; 36 | hanging-punctuation: first; 37 | } 38 | 39 | & p { 40 | font-size: calc(18 * var(--px-in-rem)); 41 | color: #6a737d; 42 | text-indent: -0.45em; 43 | quotes: "“" "”" "‘" "’"; 44 | line-height: calculateRem(28px); 45 | margin-bottom: calculateRem(18px); 46 | 47 | &::before { 48 | content: open-quote; 49 | } 50 | 51 | &::after { 52 | content: close-quote; 53 | } 54 | } 55 | 56 | & footer { 57 | font-size: calc(14 * var(--px-in-rem)); 58 | 59 | & a { 60 | font-weight: bold; 61 | line-height: calc(28 * var(--px-in-rem)); 62 | text-decoration-line: underline; 63 | color: var(--primary-bg); 64 | } 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/components/slider.module.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | overflow-x: hidden; 3 | height: 100%; 4 | width: 100%; 5 | } 6 | 7 | .slider { 8 | display: flex; 9 | align-items: center; 10 | overflow-y: hidden; 11 | align-items: center; 12 | height: 100%; 13 | transition: transform 0.2s ease-out; 14 | &.noTransition { 15 | transition: none; 16 | } 17 | } 18 | 19 | .slide { 20 | height: 100%; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/status-bar.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import Link from 'next/link'; 3 | import styles from './status-bar.module.css'; 4 | 5 | export default function StatusBar() { 6 | const [hasGpc, setHasGpc] = useState(undefined); 7 | 8 | useEffect(() => { 9 | const timeoutId = setTimeout(() => { 10 | setHasGpc(!!navigator.globalPrivacyControl); 11 | }, 50); 12 | return () => { 13 | clearTimeout(timeoutId); 14 | }; 15 | }, []); 16 | 17 | if (hasGpc === undefined) { 18 | return null; 19 | } 20 | 21 | return ( 22 |
23 |
24 |
25 |
26 | 34 | 35 | {hasGpc ? ( 36 | <> 37 | 38 | GPC signal detected. 39 | 40 | 41 | Test against the 42 | 43 | reference server 44 | 45 | . 46 | 47 | 48 | ) : ( 49 | <> 50 | 51 | GPC signal not detected. 52 | 53 | 54 | Please 55 | 56 | download a browser/extension 57 | 58 | that supports it. 59 | 60 | 61 | )} 62 | 63 |
64 |
65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/components/status-bar.module.css: -------------------------------------------------------------------------------- 1 | .statusBar { 2 | background-color: #172a24; 3 | color: white; 4 | 5 | font-size: calc(14 * var(--px-in-rem)); 6 | line-height: calc(28 * var(--px-in-rem)); 7 | 8 | & a { 9 | color: white; 10 | font-weight: 800; 11 | padding: 0 0.2rem; 12 | 13 | @media(--media-breakpoint-down-sm) { 14 | font-size: calc(13 * var(--px-in-rem)); 15 | } 16 | } 17 | } 18 | 19 | .dot { 20 | width: calc(12 * var(--px-in-rem)); 21 | height: calc(12 * var(--px-in-rem)); 22 | position: relative; 23 | top: 1px; 24 | } 25 | 26 | .dotGreen { 27 | background-color: #058A5E; 28 | } 29 | 30 | .dotRed { 31 | background-color: #de0000; 32 | } 33 | 34 | .statusText { 35 | font-weight: 400; 36 | padding-right: 0.2rem; 37 | } 38 | 39 | .statusCheck { 40 | @media(--media-breakpoint-down-sm) { 41 | display: block; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/visually-hidden.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from './visually-hidden.module.css'; 4 | 5 | /** 6 | * Helper for accessibility (children can be read by screen reader but are not visible) 7 | */ 8 | export default function VisuallyHidden({ children, ...others }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | 16 | VisuallyHidden.propTypes = { 17 | children: PropTypes.any 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/visually-hidden.module.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | border: 0; 3 | clip: rect(0 0 0 0); 4 | height: 1px; 5 | margin: -1px; 6 | overflow: hidden; 7 | padding: 0; 8 | position: absolute; 9 | width: 1px; 10 | whitespace: nowrap; 11 | wordwrap: normal; 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import Head from 'next/head'; 3 | import 'bootstrap/scss/bootstrap.scss'; 4 | import '../styles/globals.css'; 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 28 | 34 | 35 | 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | MyApp.propTypes = { 46 | Component: PropTypes.any, 47 | pageProps: PropTypes.object 48 | }; 49 | 50 | export default MyApp; 51 | -------------------------------------------------------------------------------- /src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 2 | 3 | class MyDocument extends Document { 4 | static async getInitialProps(ctx) { 5 | const initialProps = await Document.getInitialProps(ctx); 6 | return { ...initialProps }; 7 | } 8 | 9 | render() { 10 | return ( 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ); 19 | } 20 | } 21 | 22 | export default MyDocument; 23 | -------------------------------------------------------------------------------- /src/pages/faq.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import PropTypes from 'prop-types'; 3 | import { parseMarkdown } from '../utils/markdown'; 4 | import Layout from '../components/layout'; 5 | import Article from '../components/article'; 6 | import FaqList from '../components/faq-list'; 7 | 8 | export default function FaqPage({ data, html, sections }) { 9 | return ( 10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 | ); 18 | } 19 | 20 | FaqPage.propTypes = { 21 | data: PropTypes.shape({ 22 | title: PropTypes.string.isRequired 23 | }), 24 | html: PropTypes.string, 25 | sections: PropTypes.arrayOf( 26 | PropTypes.shape({ 27 | key: PropTypes.string.isRequired, 28 | data: PropTypes.shape({ 29 | title: PropTypes.string.isRequired 30 | }), 31 | html: PropTypes.string.isRequired 32 | }) 33 | ).isRequired 34 | }; 35 | 36 | export async function getStaticProps(context) { 37 | const props = await parseMarkdown( 38 | path.join(process.cwd(), 'content', 'faq.md') 39 | ); 40 | 41 | return { props }; 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import PropTypes from 'prop-types'; 3 | import Layout from '../components/layout'; 4 | import Home from '../components/home'; 5 | import { parseMarkdown } from '../utils/markdown'; 6 | 7 | export default function HomePage({ 8 | pressData, 9 | faqData, 10 | orgsData, 11 | downloadsData, 12 | testimonialsData 13 | }) { 14 | return ( 15 | 16 | 23 | 24 | ); 25 | } 26 | 27 | HomePage.propTypes = { 28 | pressData: PropTypes.shape({ 29 | data: PropTypes.shape({ 30 | title: PropTypes.string.isRequired, 31 | entries: PropTypes.arrayOf( 32 | PropTypes.shape({ 33 | datePublished: PropTypes.string.isRequired, 34 | contentUrl: PropTypes.string.isRequired, 35 | title: PropTypes.string.isRequired, 36 | source: PropTypes.string.isRequired, 37 | isFeaturedIndex: PropTypes.number, 38 | img: PropTypes.string 39 | }) 40 | ).isRequired 41 | }).isRequired 42 | }).isRequired, 43 | faqData: PropTypes.shape({ 44 | data: PropTypes.shape({ 45 | title: PropTypes.string.isRequired 46 | }), 47 | html: PropTypes.string, 48 | sections: PropTypes.arrayOf( 49 | PropTypes.shape({ 50 | key: PropTypes.string.isRequired, 51 | data: PropTypes.shape({ 52 | title: PropTypes.string.isRequired 53 | }), 54 | html: PropTypes.string.isRequired 55 | }) 56 | ).isRequired 57 | }).isRequired, 58 | orgsData: PropTypes.shape({ 59 | data: PropTypes.shape({ 60 | title: PropTypes.string.isRequired, 61 | entries: PropTypes.arrayOf( 62 | PropTypes.shape({ 63 | name: PropTypes.string.isRequired, 64 | url: PropTypes.string.isRequired, 65 | img: PropTypes.string.isRequired 66 | }) 67 | ).isRequired 68 | }).isRequired, 69 | html: PropTypes.string.isRequired 70 | }).isRequired, 71 | downloadsData: PropTypes.shape({ 72 | data: PropTypes.shape({ 73 | title: PropTypes.string.isRequired, 74 | entries: PropTypes.arrayOf( 75 | PropTypes.shape({ 76 | name: PropTypes.string.isRequired, 77 | url: PropTypes.string.isRequired, 78 | img: PropTypes.string.isRequired 79 | }) 80 | ).isRequired 81 | }).isRequired, 82 | html: PropTypes.string.isRequired 83 | }).isRequired, 84 | testimonialsData: PropTypes.shape({ 85 | data: PropTypes.shape({ 86 | entries: PropTypes.arrayOf( 87 | PropTypes.shape({ 88 | name: PropTypes.string.isRequired, 89 | position: PropTypes.string.isRequired, 90 | quote: PropTypes.string.isRequired, 91 | url: PropTypes.string.isRequired, 92 | img: PropTypes.string.isRequired 93 | }) 94 | ).isRequired 95 | }).isRequired, 96 | html: PropTypes.string.isRequired 97 | }).isRequired 98 | }; 99 | 100 | export async function getStaticProps(context) { 101 | const faqData = await parseMarkdown( 102 | path.join(process.cwd(), 'content', 'faq.md') 103 | ); 104 | const pressData = await parseMarkdown( 105 | path.join(process.cwd(), 'content', 'press.md') 106 | ); 107 | const orgsData = await parseMarkdown( 108 | path.join(process.cwd(), 'content', 'orgs.md') 109 | ); 110 | 111 | const downloadsData = await parseMarkdown( 112 | path.join(process.cwd(), 'content', 'downloads.md') 113 | ); 114 | const testimonialsData = await parseMarkdown( 115 | path.join(process.cwd(), 'content', 'testimonials.md') 116 | ); 117 | 118 | return { 119 | props: { faqData, pressData, orgsData, downloadsData, testimonialsData } 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /src/pages/orgs.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import PropTypes from 'prop-types'; 3 | import { parseMarkdown } from '../utils/markdown'; 4 | import Layout from '../components/layout'; 5 | import Article from '../components/article'; 6 | import BrowsersPlugins from '../components/browsers-plugins'; 7 | import OrgList from '../components/org-list'; 8 | import OrgsHeader from '../components/orgs-header'; 9 | import styles from './orgs.module.css'; 10 | 11 | export default function OrgsPage({ 12 | data: { title, headerText, entries } = {}, 13 | sections 14 | }) { 15 | const Business = entries.filter((e) => e.type == 'Business' || e.inOrganizations == true); 16 | return ( 17 | 18 |
19 |
20 |
21 |
22 |

{headerText}

23 |
24 |
25 | 26 |
27 | e.key == 'Browsers')[0].data} 29 | /> 30 |
31 | 32 |
33 |
34 | 35 |
36 | e.key == 'Business')[0].data} 38 | /> 39 |
40 | 41 |
42 |
43 | 44 |
45 |
46 |
47 | ); 48 | } 49 | 50 | OrgsPage.propTypes = { 51 | data: PropTypes.shape({ 52 | title: PropTypes.string.isRequired, 53 | entries: PropTypes.arrayOf( 54 | PropTypes.shape({ 55 | name: PropTypes.string.isRequired, 56 | url: PropTypes.string.isRequired, 57 | img: PropTypes.string, 58 | browser_img: PropTypes.string, 59 | type: PropTypes.string.isRequired 60 | }) 61 | ).isRequired 62 | }).isRequired, 63 | sections: PropTypes.array.isRequired 64 | }; 65 | 66 | export async function getStaticProps(context) { 67 | const props = await parseMarkdown( 68 | path.join(process.cwd(), 'content', 'orgs.md') 69 | ); 70 | 71 | return { props }; 72 | } 73 | -------------------------------------------------------------------------------- /src/pages/orgs.module.css: -------------------------------------------------------------------------------- 1 | .sectionText { 2 | font-size: calc(16 * var(--px-in-rem)); 3 | line-height: calc(28 * var(--px-in-rem)); 4 | margin-bottom: calc(97 * var(--px-in-rem)); 5 | 6 | & a { 7 | color: var(--text-dark); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/press.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import PropTypes from 'prop-types'; 3 | import { parseMarkdown } from '../utils/markdown'; 4 | import Layout from '../components/layout'; 5 | import Article from '../components/article'; 6 | import PressList from '../components/press-list'; 7 | import FeaturedArticles from '../components/featured-articles'; 8 | import styles from './press.module.css'; 9 | 10 | export default function PressPage({ data: { title, entries } = {} }) { 11 | return ( 12 | 13 |
14 |
15 |
16 |

Featured Articles

17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | 28 | PressPage.propTypes = { 29 | data: PropTypes.shape({ 30 | title: PropTypes.string.isRequired, 31 | entries: PropTypes.arrayOf( 32 | PropTypes.shape({ 33 | datePublished: PropTypes.string.isRequired, 34 | contentUrl: PropTypes.string.isRequired, 35 | title: PropTypes.string.isRequired, 36 | source: PropTypes.string.isRequired, 37 | isFeaturedIndex: PropTypes.number, 38 | img: PropTypes.string 39 | }) 40 | ).isRequired 41 | }).isRequired 42 | }; 43 | 44 | export async function getStaticProps(context) { 45 | const props = await parseMarkdown( 46 | path.join(process.cwd(), 'content', 'press.md') 47 | ); 48 | 49 | return { props }; 50 | } 51 | -------------------------------------------------------------------------------- /src/pages/press.module.css: -------------------------------------------------------------------------------- 1 | .featured { 2 | margin: 0 0 2em; 3 | & header { 4 | margin: 2em 0; 5 | text-align: center; 6 | & h2 { 7 | font-weight: 900; 8 | font-size: calc(10 * var(--px-in-rem)); 9 | line-height: 1; 10 | letter-spacing: 0.2em; 11 | color: var(--text-light); 12 | text-transform: uppercase; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/styles/fonts.css: -------------------------------------------------------------------------------- 1 | /* Source: https://google-webfonts-helper.herokuapp.com/fonts/roboto?subsets=latin */ 2 | 3 | /* roboto-regular - latin */ 4 | @font-face { 5 | font-family: 'Roboto'; 6 | font-style: normal; 7 | font-weight: 400; 8 | font-display: swap; 9 | src: local('Roboto'), local('Roboto-Regular'), 10 | url('/fonts/roboto-v20-latin-regular.woff2') format('woff2'), 11 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 12 | url('/fonts/roboto-v20-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 13 | } 14 | 15 | /* roboto-italic - latin */ 16 | @font-face { 17 | font-family: 'Roboto'; 18 | font-style: italic; 19 | font-weight: 400; 20 | src: local('Roboto Italic'), local('Roboto-Italic'), 21 | url('/fonts/roboto-v20-latin-italic.woff2') format('woff2'), 22 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 23 | url('/fonts/roboto-v20-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 24 | } 25 | 26 | /* roboto-700 - latin */ 27 | @font-face { 28 | font-family: 'Roboto'; 29 | font-style: normal; 30 | font-weight: 700; 31 | font-display: swap; 32 | src: local('Roboto Bold'), local('Roboto-Bold'), 33 | url('/fonts/roboto-v20-latin-700.woff2') format('woff2'), 34 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 35 | url('/fonts/roboto-v20-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 36 | } 37 | 38 | /* roboto-700italic - latin */ 39 | @font-face { 40 | font-family: 'Roboto'; 41 | font-style: italic; 42 | font-weight: 700; 43 | font-display: swap; 44 | src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'), 45 | url('/fonts/roboto-v20-latin-700italic.woff2') format('woff2'), 46 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 47 | url('/fonts/roboto-v20-latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 48 | } 49 | 50 | /* roboto-900 - latin */ 51 | @font-face { 52 | font-family: 'Roboto'; 53 | font-style: normal; 54 | font-weight: 900; 55 | font-display: swap; 56 | src: local('Roboto Black'), local('Roboto-Black'), 57 | url('/fonts/roboto-v20-latin-900.woff2') format('woff2'), 58 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 59 | url('/fonts/roboto-v20-latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 60 | } 61 | 62 | /* roboto-900italic - latin */ 63 | @font-face { 64 | font-family: 'Roboto'; 65 | font-style: italic; 66 | font-weight: 900; 67 | font-display: swap; 68 | src: local('Roboto Black Italic'), local('Roboto-BlackItalic'), 69 | url('/fonts/roboto-v20-latin-900italic.woff2') format('woff2'), 70 | /* Chrome 26+, Opera 23+, Firefox 39+ */ 71 | url('/fonts/roboto-v20-latin-900italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 72 | } 73 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import './fonts.css'; 2 | @import './variables.css'; 3 | @import 'highlight.js/styles/default.css'; 4 | 5 | html { 6 | scroll-behavior: smooth; 7 | } 8 | 9 | body { 10 | color: white; 11 | font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", 12 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | button:hover { 16 | cursor: pointer; 17 | } 18 | 19 | a { 20 | text-decoration: none; 21 | 22 | &:hover { 23 | text-decoration: underline; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/styles/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color: red; 3 | --px-in-rem: 0.0625rem; /* 1/16 */ 4 | 5 | --bg-dark: #0a3e2d; 6 | --bg-light: #35AB85; 7 | --text-light: #999999; 8 | --text-dark: #49504e; 9 | --text-bright-green: #25bf87; 10 | 11 | --focus-outline: rgba(0, 123, 255, 0.25); 12 | --primary-bg: #058a5e; 13 | --primary-bg-hover: rgba(5, 138, 94, 0.7); 14 | } 15 | 16 | @custom-media --media-breakpoint-up-sm (min-width: 576px); 17 | @custom-media --media-breakpoint-up-md (min-width: 768px); 18 | @custom-media --media-breakpoint-up-lg (min-width: 992px); 19 | @custom-media --media-breakpoint-up-xl(min-width: 1200px); 20 | 21 | @custom-media --media-breakpoint-down-xs (max-width: 575.98px); 22 | @custom-media --media-breakpoint-down-sm (max-width: 767.98px); 23 | @custom-media --media-breakpoint-down-md (max-width: 991.98px); 24 | @custom-media --media-breakpoint-down-lg (max-width: 1199.98px); 25 | 26 | @custom-media --media-breakpoint-only-xs (max-width: 575.98px); 27 | @custom-media --media-breakpoint-only-sm (min-width: 576px) and (max-width: 767.98px); 28 | @custom-media --media-breakpoint-only-md (min-width: 768px) and (max-width: 991.98px); 29 | @custom-media --media-breakpoint-only-lg (min-width: 992px) and (max-width: 1199.98px); 30 | @custom-media --media-breakpoint-only-xl (min-width: 1200px); 31 | -------------------------------------------------------------------------------- /src/utils/markdown.js: -------------------------------------------------------------------------------- 1 | import { readFile } from 'fs/promises'; 2 | import yaml from 'js-yaml'; 3 | import matter from 'gray-matter'; 4 | import { remark } from 'remark'; 5 | import remarkHtml from 'remark-html'; 6 | 7 | const mdProcessor = remark().use(remarkHtml); 8 | 9 | export async function parseMarkdown(filePath) { 10 | const markdown = await readFile(filePath, 'utf8'); 11 | 12 | const { data, content, sections } = matter(markdown, { 13 | section: function (section) { 14 | if (typeof section.data === 'string' && section.data.trim() !== '') { 15 | section.data = yaml.load(section.data); 16 | } 17 | section.content = section.content.trim(); 18 | }, 19 | excerp: true 20 | }); 21 | 22 | let html = ''; 23 | if (content && content.trim() !== '') { 24 | html = (await mdProcessor.process(content.trim())).toString(); 25 | } 26 | 27 | if (sections) { 28 | for (const section of sections) { 29 | if (section.content && section.content.trim() !== '') { 30 | section.html = ( 31 | await mdProcessor.process(section.content.trim()) 32 | ).toString(); 33 | delete section.content; 34 | } 35 | } 36 | } 37 | 38 | return { data, html, sections }; 39 | } 40 | --------------------------------------------------------------------------------