├── .editorconfig
├── .github
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── config.yml
├── auto-build-maybe.yml.txt
├── logo-dark.svg
├── logo-light.svg
└── workflows
│ └── BuildCSS.yml
├── .gitignore
├── .node-version
├── .nvmrc
├── .prettierrc
├── LICENSE.md
├── README.md
├── composer.json
├── css
├── pico.amber.css
├── pico.amber.min.css
├── pico.azure.css
├── pico.azure.min.css
├── pico.blue.css
├── pico.blue.min.css
├── pico.classless.amber.css
├── pico.classless.amber.min.css
├── pico.classless.azure.css
├── pico.classless.azure.min.css
├── pico.classless.blue.css
├── pico.classless.blue.min.css
├── pico.classless.conditional.amber.css
├── pico.classless.conditional.amber.min.css
├── pico.classless.conditional.azure.css
├── pico.classless.conditional.azure.min.css
├── pico.classless.conditional.blue.css
├── pico.classless.conditional.blue.min.css
├── pico.classless.conditional.cyan.css
├── pico.classless.conditional.cyan.min.css
├── pico.classless.conditional.fuchsia.css
├── pico.classless.conditional.fuchsia.min.css
├── pico.classless.conditional.green.css
├── pico.classless.conditional.green.min.css
├── pico.classless.conditional.grey.css
├── pico.classless.conditional.grey.min.css
├── pico.classless.conditional.indigo.css
├── pico.classless.conditional.indigo.min.css
├── pico.classless.conditional.jade.css
├── pico.classless.conditional.jade.min.css
├── pico.classless.conditional.lime.css
├── pico.classless.conditional.lime.min.css
├── pico.classless.conditional.orange.css
├── pico.classless.conditional.orange.min.css
├── pico.classless.conditional.pink.css
├── pico.classless.conditional.pink.min.css
├── pico.classless.conditional.pumpkin.css
├── pico.classless.conditional.pumpkin.min.css
├── pico.classless.conditional.purple.css
├── pico.classless.conditional.purple.min.css
├── pico.classless.conditional.red.css
├── pico.classless.conditional.red.min.css
├── pico.classless.conditional.sand.css
├── pico.classless.conditional.sand.min.css
├── pico.classless.conditional.slate.css
├── pico.classless.conditional.slate.min.css
├── pico.classless.conditional.violet.css
├── pico.classless.conditional.violet.min.css
├── pico.classless.conditional.yellow.css
├── pico.classless.conditional.yellow.min.css
├── pico.classless.conditional.zinc.css
├── pico.classless.conditional.zinc.min.css
├── pico.classless.css
├── pico.classless.cyan.css
├── pico.classless.cyan.min.css
├── pico.classless.fuchsia.css
├── pico.classless.fuchsia.min.css
├── pico.classless.green.css
├── pico.classless.green.min.css
├── pico.classless.grey.css
├── pico.classless.grey.min.css
├── pico.classless.indigo.css
├── pico.classless.indigo.min.css
├── pico.classless.jade.css
├── pico.classless.jade.min.css
├── pico.classless.lime.css
├── pico.classless.lime.min.css
├── pico.classless.min.css
├── pico.classless.orange.css
├── pico.classless.orange.min.css
├── pico.classless.pink.css
├── pico.classless.pink.min.css
├── pico.classless.pumpkin.css
├── pico.classless.pumpkin.min.css
├── pico.classless.purple.css
├── pico.classless.purple.min.css
├── pico.classless.red.css
├── pico.classless.red.min.css
├── pico.classless.sand.css
├── pico.classless.sand.min.css
├── pico.classless.slate.css
├── pico.classless.slate.min.css
├── pico.classless.violet.css
├── pico.classless.violet.min.css
├── pico.classless.yellow.css
├── pico.classless.yellow.min.css
├── pico.classless.zinc.css
├── pico.classless.zinc.min.css
├── pico.colors.css
├── pico.colors.min.css
├── pico.conditional.amber.css
├── pico.conditional.amber.min.css
├── pico.conditional.azure.css
├── pico.conditional.azure.min.css
├── pico.conditional.blue.css
├── pico.conditional.blue.min.css
├── pico.conditional.css
├── pico.conditional.cyan.css
├── pico.conditional.cyan.min.css
├── pico.conditional.fuchsia.css
├── pico.conditional.fuchsia.min.css
├── pico.conditional.green.css
├── pico.conditional.green.min.css
├── pico.conditional.grey.css
├── pico.conditional.grey.min.css
├── pico.conditional.indigo.css
├── pico.conditional.indigo.min.css
├── pico.conditional.jade.css
├── pico.conditional.jade.min.css
├── pico.conditional.lime.css
├── pico.conditional.lime.min.css
├── pico.conditional.min.css
├── pico.conditional.orange.css
├── pico.conditional.orange.min.css
├── pico.conditional.pink.css
├── pico.conditional.pink.min.css
├── pico.conditional.pumpkin.css
├── pico.conditional.pumpkin.min.css
├── pico.conditional.purple.css
├── pico.conditional.purple.min.css
├── pico.conditional.red.css
├── pico.conditional.red.min.css
├── pico.conditional.sand.css
├── pico.conditional.sand.min.css
├── pico.conditional.slate.css
├── pico.conditional.slate.min.css
├── pico.conditional.violet.css
├── pico.conditional.violet.min.css
├── pico.conditional.yellow.css
├── pico.conditional.yellow.min.css
├── pico.conditional.zinc.css
├── pico.conditional.zinc.min.css
├── pico.css
├── pico.cyan.css
├── pico.cyan.min.css
├── pico.fluid.classless.amber.css
├── pico.fluid.classless.amber.min.css
├── pico.fluid.classless.azure.css
├── pico.fluid.classless.azure.min.css
├── pico.fluid.classless.blue.css
├── pico.fluid.classless.blue.min.css
├── pico.fluid.classless.conditional.amber.css
├── pico.fluid.classless.conditional.amber.min.css
├── pico.fluid.classless.conditional.azure.css
├── pico.fluid.classless.conditional.azure.min.css
├── pico.fluid.classless.conditional.blue.css
├── pico.fluid.classless.conditional.blue.min.css
├── pico.fluid.classless.conditional.css
├── pico.fluid.classless.conditional.cyan.css
├── pico.fluid.classless.conditional.cyan.min.css
├── pico.fluid.classless.conditional.fuchsia.css
├── pico.fluid.classless.conditional.fuchsia.min.css
├── pico.fluid.classless.conditional.green.css
├── pico.fluid.classless.conditional.green.min.css
├── pico.fluid.classless.conditional.grey.css
├── pico.fluid.classless.conditional.grey.min.css
├── pico.fluid.classless.conditional.indigo.css
├── pico.fluid.classless.conditional.indigo.min.css
├── pico.fluid.classless.conditional.jade.css
├── pico.fluid.classless.conditional.jade.min.css
├── pico.fluid.classless.conditional.lime.css
├── pico.fluid.classless.conditional.lime.min.css
├── pico.fluid.classless.conditional.min.css
├── pico.fluid.classless.conditional.orange.css
├── pico.fluid.classless.conditional.orange.min.css
├── pico.fluid.classless.conditional.pink.css
├── pico.fluid.classless.conditional.pink.min.css
├── pico.fluid.classless.conditional.pumpkin.css
├── pico.fluid.classless.conditional.pumpkin.min.css
├── pico.fluid.classless.conditional.purple.css
├── pico.fluid.classless.conditional.purple.min.css
├── pico.fluid.classless.conditional.red.css
├── pico.fluid.classless.conditional.red.min.css
├── pico.fluid.classless.conditional.sand.css
├── pico.fluid.classless.conditional.sand.min.css
├── pico.fluid.classless.conditional.slate.css
├── pico.fluid.classless.conditional.slate.min.css
├── pico.fluid.classless.conditional.violet.css
├── pico.fluid.classless.conditional.violet.min.css
├── pico.fluid.classless.conditional.yellow.css
├── pico.fluid.classless.conditional.yellow.min.css
├── pico.fluid.classless.conditional.zinc.css
├── pico.fluid.classless.conditional.zinc.min.css
├── pico.fluid.classless.css
├── pico.fluid.classless.cyan.css
├── pico.fluid.classless.cyan.min.css
├── pico.fluid.classless.fuchsia.css
├── pico.fluid.classless.fuchsia.min.css
├── pico.fluid.classless.green.css
├── pico.fluid.classless.green.min.css
├── pico.fluid.classless.grey.css
├── pico.fluid.classless.grey.min.css
├── pico.fluid.classless.indigo.css
├── pico.fluid.classless.indigo.min.css
├── pico.fluid.classless.jade.css
├── pico.fluid.classless.jade.min.css
├── pico.fluid.classless.lime.css
├── pico.fluid.classless.lime.min.css
├── pico.fluid.classless.min.css
├── pico.fluid.classless.orange.css
├── pico.fluid.classless.orange.min.css
├── pico.fluid.classless.pink.css
├── pico.fluid.classless.pink.min.css
├── pico.fluid.classless.pumpkin.css
├── pico.fluid.classless.pumpkin.min.css
├── pico.fluid.classless.purple.css
├── pico.fluid.classless.purple.min.css
├── pico.fluid.classless.red.css
├── pico.fluid.classless.red.min.css
├── pico.fluid.classless.sand.css
├── pico.fluid.classless.sand.min.css
├── pico.fluid.classless.slate.css
├── pico.fluid.classless.slate.min.css
├── pico.fluid.classless.violet.css
├── pico.fluid.classless.violet.min.css
├── pico.fluid.classless.yellow.css
├── pico.fluid.classless.yellow.min.css
├── pico.fluid.classless.zinc.css
├── pico.fluid.classless.zinc.min.css
├── pico.fuchsia.css
├── pico.fuchsia.min.css
├── pico.green.css
├── pico.green.min.css
├── pico.grey.css
├── pico.grey.min.css
├── pico.indigo.css
├── pico.indigo.min.css
├── pico.jade.css
├── pico.jade.min.css
├── pico.lime.css
├── pico.lime.min.css
├── pico.min.css
├── pico.orange.css
├── pico.orange.min.css
├── pico.pink.css
├── pico.pink.min.css
├── pico.pumpkin.css
├── pico.pumpkin.min.css
├── pico.purple.css
├── pico.purple.min.css
├── pico.red.css
├── pico.red.min.css
├── pico.sand.css
├── pico.sand.min.css
├── pico.slate.css
├── pico.slate.min.css
├── pico.violet.css
├── pico.violet.min.css
├── pico.yellow.css
├── pico.yellow.min.css
├── pico.zinc.css
├── pico.zinc.min.css
└── postcss.config.js
├── docs
├── .editorconfig
├── basics.css
├── favicon-152x152.png
├── favicon-167x167.png
├── favicon-16x16.png
├── favicon-180x180.png
├── favicon-192x192.png
├── favicon-32x32.png
├── favicon-48x48.png
├── favicon-512x512.png
├── favicon.ico
├── favicon.svg
├── index.html
├── js
│ ├── FileValidator.js
│ ├── Modal.js
│ ├── PicoTabs.js
│ ├── SwitchColorMode.js
│ └── old
│ │ └── MinimalThemeSwitcher.js
├── manifest.json
├── pico.colors.min.css
├── pico.css
└── pico.min.css
├── package.json
├── scripts
├── build-dev.js
├── build-themes.js
├── copy-docs-css-files-dev.js
└── copy-docs-css-files.js
└── scss
├── _index.scss
├── _settings.scss
├── colors
├── _index.scss
└── utilities
│ ├── _background-colors.scss
│ ├── _colors.scss
│ ├── _css-vars.scss
│ ├── _index.scss
│ ├── _settings.scss
│ └── _utils.scss
├── components
├── _accordion.scss
├── _card.scss
├── _dropdown.scss
├── _group.scss
├── _loading.scss
├── _modal.scss
├── _nav-hamburger.scss
├── _nav.scss
├── _notification.scss
├── _popover-toast.scss
├── _popover.scss
├── _progress.scss
├── _tab-region.scss
├── _tab.scss
├── _timeline.scss
└── _tooltip.scss
├── content
├── _button.scss
├── _code.scss
├── _embedded.scss
├── _figure.scss
├── _link.scss
├── _misc.scss
├── _table.scss
└── _typography.scss
├── forms
├── _basics.scss
├── _checkbox-radio-switch.scss
├── _floating.scss
├── _input-color.scss
├── _input-date.scss
├── _input-file.scss
├── _input-range.scss
├── _input-search.scss
└── _validation.scss
├── helpers
├── _copyright.scss
└── _functions.scss
├── layout
├── _container.scss
├── _document.scss
├── _grid.scss
├── _landmarks.scss
├── _overflow-auto.scss
├── _row.scss
└── _section.scss
├── pico.classless.scss
├── pico.colors.scss
├── pico.conditional.scss
├── pico.fluid.classless.conditional.scss
├── pico.fluid.classless.scss
├── pico.scss
├── postcss.config.js
├── themes
├── _default.scss
└── default
│ ├── _dark.scss
│ ├── _light.scss
│ ├── _schemes.scss
│ ├── _styles.scss
│ └── _theme-colors.scss
└── utilities
├── _accessibility.scss
└── _reduce-motion.scss
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = crlf
9 | insert_final_newline = true
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | indent_style = space
13 | indent_size = 2
14 |
15 | # JavaScript and JSON files
16 | [*.{js,json}]
17 | indent_size = 2
18 |
19 | # SCSS files
20 | [*.scss]
21 | indent_size = 2
22 |
23 | # Markdown files
24 | [*.md]
25 | trim_trailing_whitespace = false
26 |
27 | # Package files
28 | [package.json]
29 | indent_size = 2
30 |
31 | # Config files
32 | [*.{yml,yaml}]
33 | indent_size = 2
34 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Pico
2 |
3 | Thanks for your interest in contributing to Pico CSS! Please take a moment to review this document before submitting a [bug report](https://github.com/Yohn/PicoCSS/issues) or a [pull request](https://github.com/Yohn/PicoCSS/pulls).
4 |
5 | ## Bug reports
6 |
7 | The [issue tracker]((https://github.com/Yohn/PicoCSS/issues)) is the preferred channel for bug reports, but please respect the following restrictions:
8 | - Please do not use the issue tracker for personal support requests. [Open a question in our discussion forums](https://github.com/Yohn/PicoCSS/discussions/categories/help) instead.
9 | - Please do not use the issue tracker for feature requests. Instead, use our discussion forums to [suggest any ideas](https://github.com/Yohn/PicoCSS/discussions/categories/ideas) you have.
10 |
11 | ## Pull requests
12 |
13 | Good pull requests, patches, improvements, and new features are a fantastic help.
14 |
15 | **Please ask before starting work on any significant new features.**
16 | We recommend that you first [suggest your feature idea in our discussion forums](https://github.com/Yohn/PicoCSS/discussions/categories/ideas).
17 |
18 |
21 |
22 | **Do not edit [`/css`](https://github.com/Yohn/PicoCSS/tree/master/css) files directly.**
23 |
24 | Edit the source files in [`/scss`](https://github.com/Yohn/PicoCSS/tree/master/scss), Github will automatically re-compile the css files after the pull request is merged.
25 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [Yohn] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | #patreon: # Replace with a single Patreon username
5 | #open_collective: # Replace with a single Open Collective username
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | #liberapay: # Replace with a single Liberapay username
10 | #issuehunt: # Replace with a single IssueHunt username
11 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | #polar: # Replace with a single Polar username
13 | #buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | #thanks_dev: # Replace with a single thanks.dev username
15 | custom: ['http://cash.me/$yohnjohn84'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report if you've already asked for help with a problem and confirmed something is broken with Pico CSS.
4 | ---
5 |
6 | Please search for duplicate or closed issues first.
7 |
8 | ## Describe the issue
9 |
10 | ### Current Behavior
11 | A concise description of the bug.
12 |
13 | ### Expected Behavior
14 | A concise description of what you expected.
15 |
16 | ### Reproduction URL
17 | We recommend including a link to a minimal reproduction of the bug using CodePen or a similar tool.
18 | **Please do not link to your actual project.** Instead, we need a reduced test case in a new project without any unnecessary code.
19 |
20 | ### Environment
21 | Example: OS, versions, browser details.
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Get Help
4 | url: https://github.com/Yohn/PicoCSS/discussions/categories/help
5 | about: If you can't get something to work the way you expect, open a question in our discussion forums.
6 | - name: Feature Request
7 | url: https://github.com/Yohn/PicoCSS/discussions/categories/ideas
8 | about: Suggest any ideas you have using our discussion forums.
9 |
--------------------------------------------------------------------------------
/.github/auto-build-maybe.yml.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/logo-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.github/logo-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.github/workflows/BuildCSS.yml:
--------------------------------------------------------------------------------
1 | name: Build CSS
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'scss/**'
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 |
14 | permissions:
15 | contents: write
16 |
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v2
20 |
21 | - name: Set up Node.js
22 | uses: actions/setup-node@v2
23 | with:
24 | node-version: '20'
25 |
26 | - name: Install dependencies
27 | run: npm install
28 |
29 | - name: Build CSS
30 | run: npm run build
31 |
32 | - name: Commit and push changes
33 | run: |
34 | git config --global user.name 'github-actions[bot]'
35 | git config --global user.email 'github-actions[bot]@users.noreply.github.com'
36 | git add .
37 | git commit -m 'Build CSS'
38 | git push
39 | env:
40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS or Editor folders
2 | ._*
3 | .cache
4 | .DS_Store
5 | .idea
6 | .project
7 | .settings
8 | .tmproj
9 | *.esproj
10 | *.sublime-project
11 | *.sublime-workspace
12 | nbproject
13 | Thumbs.db
14 | /.vscode/
15 |
16 | # Numerous always-ignore extensions
17 | *.diff
18 | *.err
19 | *.log
20 | *.orig
21 | *.rej
22 | *.swo
23 | *.swp
24 | *.vi
25 | *.zip
26 | *~
27 |
28 | # Folders to ignore
29 | /node_modules/
30 |
31 | # Pico
32 | .pico
33 | package-lock.json
34 | /docs/orig
35 | /scss/components/_offcanvas.scss
36 | docs/js/DialogManager.js
37 | docs/modal.html
38 |
39 | /.yohn
40 | _docs/
41 |
42 | /zzz
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 20
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2024 Pico
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | [](https://github.com/Yohn/PicoCSS/releases/latest)
12 | [](https://www.npmjs.com/package/@yohns/picocss)
13 | [](https://github.com/Yohn/PicoCSS/blob/master/LICENSE.md)
14 |
15 |
16 | ## Yohns Updated Version
17 | I'm not sure if the original [Pico CSS](https://github.com/picocss/pico) repository is abandoned or not, but I really liked what they had to offer, and wanted to help not let this awesomely simple and easy to use front end framework disappear, so I merged as many of open pull requests that fixed some issues, and / or enhanced the project that were available at the time. I'll try to help keep it viable and do some bug fixes if any arise, and would always appreciate anyone elses help to continue keeping this alive!
18 |
19 | You can see the new features I, and many others have created pull requests for by going to [Yohns Pico CSS](https://yohn.github.io/PicoCSS). This page just has the demos of most of the features I have merged, or added to the project.
20 |
21 | ## Extras Built on top of PicoCSS
22 | - [Alert, Confirm, and Prompt Dialogs](https://github.com/Yohn/PicoCSS-Datatables/blob/main/src/CustomDialog.js) (Will work on that more later, but it works good!)
23 | - [YoSelect](https://github.com/Yohn/YoSelect) Searchable `` options, with tagging, image support, and a lot more!
24 | - [PicoCSS-WYSIWYG](https://github.com/Yohn/PicoCSS-WYSIWYG) Simple editor wih card, accordion, image, links, lists, headers and more
25 | - [PicoCSS-Datatables](https://github.com/Yohn/PicoCSS-Datatables) Searchable, sortable, editable, filterable table data helper.
26 | - [PicoCSS-Tag-Select](https://github.com/Yohn/PicoCSS-Tag-Select) Simplier tag class to change a select tag into tags
27 |
28 | ## Minimal CSS Framework for Semantic HTML
29 |
30 | A minimalist and lightweight starter kit that prioritizes semantic syntax, making every HTML element responsive and elegant by default.
31 |
32 | Write HTML, Add Pico CSS, and Voilà!
33 |
34 | ## What’s new in v2?
35 |
36 | Pico v2.0 features better accessibility, easier customization with SASS, a complete color palette, a new group component, and 20 precompiled color themes totaling over 100 combinations accessible via CDN.
37 |
38 | [Read more](https://picocss.com/docs/v2)
39 |
40 | ## A Superpowered HTML Reset
41 |
42 | With just the right amount of everything, Pico is a great starting point for a clean and lightweight design system.
43 |
44 | - Class-light and Semantic
45 | - Great Styles with Just CSS
46 | - Responsive Everything
47 | - Light or Dark Mode
48 | - Easy Customization
49 | - Optimized Performance
50 |
51 | ## Table of contents
52 |
53 | 1. [Yohns Updated Version](#yohns-updated-version)
54 | 2. [Extras Built on top of PicoCSS](#extras-built-on-top-of-picocss)
55 | 3. [Minimal CSS Framework for Semantic HTML](#minimal-css-framework-for-semantic-html)
56 | 4. [What’s new in v2?](#whats-new-in-v2)
57 | 5. [A Superpowered HTML Reset](#a-superpowered-html-reset)
58 | 6. [Table of contents](#table-of-contents)
59 | 7. [Quick start](#quick-start)
60 | 1. [Install manually](#install-manually)
61 | 2. [Usage from CDN](#usage-from-cdn)
62 | 3. [Install with NPM](#install-with-npm)
63 | 4. [Starter HTML template](#starter-html-template)
64 | 8. [Class-less version](#class-less-version)
65 | 9. [Limitations](#limitations)
66 | 10. [Documentation](#documentation)
67 | 11. [Browser Support](#browser-support)
68 | 12. [Contributing](#contributing)
69 | 13. [Copyright and license](#copyright-and-license)
70 |
71 | ## Quick start
72 |
73 | There are 4 ways to get started with pico.css:
74 |
75 | ### Install manually
76 |
77 | [Download Pico](https://github.com/Yohn/PicoCSS/archive/refs/heads/main.zip) and link `/css/pico.min.css` in the `` of your website.
78 |
79 | ```html
80 |
81 | ```
82 |
83 | ### Usage from CDN
84 |
85 | Alternatively, you can use [jsDelivr CDN](https://www.jsdelivr.com/package/npm/@yohns/picocss) to link pico.css.
86 |
87 | ```html
88 |
89 | ```
90 |
91 | ### Install with NPM
92 |
93 | ```shell
94 | npm i @yohns/picocss
95 | ```
96 |
102 |
103 | Then, import Pico into your SCSS file with [@use](https://sass-lang.com/documentation/at-rules/use):
104 |
105 | ```SCSS
106 | @use "pico";
107 | ```
108 |
109 |
114 |
115 | ### Starter HTML template
116 |
117 | ```HTML
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | Hello world!
126 |
127 |
128 |
129 | Hello world!
130 |
131 |
132 |
133 | ```
134 |
135 | ## Class-less version
136 |
137 | Pico provides a `.classless` version.
138 |
139 | In this version, ``, ``, and `` inside `` act as containers to define a centered or a fluid viewport.
140 |
141 | Use the default `.classless` version if you need centered viewports:
142 |
143 | ```html
144 |
148 | ```
149 |
150 | Or use the `.fluid.classless` version if you need a fluid container:
151 |
152 | ```html
153 |
157 | ```
158 |
159 | Then just write pure HTML, and it should look great:
160 |
161 | ```html
162 |
163 |
164 |
165 |
166 |
167 |
168 |
172 | Hello, world!
173 |
174 |
175 |
176 | Hello, world!
177 |
178 |
179 |
180 | ```
181 |
182 | ## Limitations
183 |
184 | Pico CSS can be used without custom CSS for quick or small projects. However, it’s designed as a starting point, like a “reset CSS on steroids”. As Pico does not integrate any helpers or utilities `.classes`, this minimal CSS framework requires SCSS or CSS knowledge to build large projects.
185 |
186 | [Read more](https://picocss.com/docs/usage-scenarios)
187 |
188 | ## Documentation
189 |
190 | **Getting started**
191 |
192 | - [Yohns Pico CSS Additions](https://yohn.github.io/PicoCSS/)
193 | - [Quick start](https://picocss.com/docs)
194 | - [Version picker `New`](https://picocss.com/docs/version-picker)
195 | - [Color schemes](https://picocss.com/docs/color-schemes)
196 | - [Class-less version](https://picocss.com/docs/classless)
197 | - [Conditional styling `New`](https://picocss.com/docs/conditional)
198 | - [RTL](https://picocss.com/docs/rtl)
199 |
200 | **Customization**
201 |
202 | - [CSS Variables](https://picocss.com/docs/css-variables)
203 | - [Sass](https://picocss.com/docs/sass)
204 | - [Colors `New`](https://picocss.com/docs/colors)
205 |
206 | **Layout**
207 |
208 | - [Container](https://picocss.com/docs/container)
209 | - [Landmarks & section](https://picocss.com/docs/landmarks-section)
210 | - [Grid](https://picocss.com/docs/grid)
211 | - [Overflow auto `New`](https://picocss.com/docs/overflow-auto)
212 |
213 | **Content**
214 |
215 | - [Typography](https://picocss.com/docs/typography)
216 | - [Link](https://picocss.com/docs/link)
217 | - [Button](https://picocss.com/docs/button)
218 | - [Table](https://picocss.com/docs/table)
219 |
220 | **Forms**
221 |
222 | - [Overview](https://picocss.com/docs/forms)
223 | - [Input](https://picocss.com/docs/forms/input)
224 | - [Textarea](https://picocss.com/docs/forms/textarea)
225 | - [Select](https://picocss.com/docs/forms/select)
226 | - [Checkboxes](https://picocss.com/docs/forms/checkboxes)
227 | - [Radios](https://picocss.com/docs/forms/radios)
228 | - [Switch](https://picocss.com/docs/forms/switch)
229 | - [Range](https://picocss.com/docs/forms/range)
230 |
231 | **Components**
232 |
233 | - [Accordion](https://picocss.com/docs/accordion)
234 | - [Card](https://picocss.com/docs/card)
235 | - [Dropdown](https://picocss.com/docs/dropdown)
236 | - [Group `New`](https://picocss.com/docs/group)
237 | - [Loading](https://picocss.com/docs/loading)
238 | - [Modal](https://picocss.com/docs/modal)
239 | - [Nav](https://picocss.com/docs/nav)
240 | - [Progress](https://picocss.com/docs/progress)
241 | - [Tooltip](https://picocss.com/docs/tooltip)
242 |
243 | **About**
244 |
245 | - [What’s new in v2?](https://picocss.com/docs/v2)
246 | - [Mission](https://picocss.com/docs/mission)
247 | - [Usage scenarios](https://picocss.com/docs/usage-scenarios)
248 | - [Brand](https://picocss.com/docs/brand)
249 | - [Built With](https://picocss.com/docs/built-with)
250 |
251 | ## Browser Support
252 |
253 | Pico CSS is designed and tested for the latest stable Chrome, Firefox, Edge, and Safari releases. It does not support any version of IE, including IE 11.
254 |
255 | ## Contributing
256 |
257 | If you are interested in contributing to Pico CSS, please read our [contributing guidelines](https://github.com/Yohn/PicoCSS/blob/master/.github/CONTRIBUTING.md).
258 |
259 | ## Copyright and license
260 |
261 | Licensed under the [MIT License](https://github.com/Yohn/PicoCSS/blob/master/LICENSE.md).
262 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yohn/picocss",
3 | "description": "Minimal CSS Framework for semantic HTML.",
4 | "keywords": [
5 | "css",
6 | "css-framework",
7 | "dark-mode",
8 | "dark-theme",
9 | "lightweight",
10 | "minimal",
11 | "minimalist",
12 | "minimalistic",
13 | "native-html",
14 | "scss-framework",
15 | "semantic"
16 | ],
17 | "homepage": "https://github.com/Yohn/PicoCSS",
18 | "authors": [
19 | {
20 | "name": "Lucas Larroche",
21 | "email": "lucas@larroche.com",
22 | "homepage": "https://lucaslarroche.com",
23 | "role": "Developer"
24 | },
25 | {
26 | "name": "John Brittain III",
27 | "homepage": "https://github.com/Yohn/PicoCSS",
28 | "role": "Developer"
29 | }
30 | ],
31 | "support": {
32 | "issues": "https://github.com/Yohn/PicoCSS/issues/"
33 | },
34 | "license": "MIT"
35 | }
36 |
--------------------------------------------------------------------------------
/css/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | map: false,
3 | plugins: {
4 | autoprefixer: {
5 | cascade: false
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/docs/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = tab
8 | indent_size = 2
9 | end_of_line = crlf
10 | charset = utf-8
11 | trim_trailing_whitespace = false
12 | insert_final_newline = false
--------------------------------------------------------------------------------
/docs/basics.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Yohn's PicoCSS basics
3 | *
4 | * I'm starting this here, for now, and will probably add it to the main releases when I build up enough
5 | * useful universal classes.
6 | *
7 | * @author John Brittain III
8 | * @link https://yohn.github.io/PicoCSS
9 | */
10 |
11 | :host, :root {
12 | --yoco-spacing-block-1: calc(var(--pico-block-spacing-vertical) * .5);
13 | --yoco-spacing-inline-1: calc(var(--pico-block-spacing-horizontal) * .5);
14 | --yoco-spacing-block-2: var(--pico-block-spacing-vertical);
15 | --yoco-spacing-inline-2: var(--pico-block-spacing-horizontal);
16 | --yoco-spacing-block-3: calc(var(--pico-block-spacing-vertical) * 1.5);
17 | --yoco-spacing-inline-3: calc(var(--pico-block-spacing-horizontal) * 1.5);
18 | }
19 |
20 | .m-0 { margin: 0 !important; }
21 | .mt-0 { margin-block-start: 0 !important; }
22 | .mb-0 { margin-block-end: 0 !important; }
23 | .my-0 { margin-block: 0 !important; }
24 | .ms-0 { margin-inline-start: 0 !important; }
25 | .me-0 { margin-inline-end: 0 !important; }
26 | .mx-0 { margin-inline: 0 !important; }
27 |
28 | .m-1 { margin: var(--yoco-spacing-block-1) var(--yoco-spacing-inline-1) !important; }
29 | .mt-1 { margin-block-start: var(--yoco-spacing-block-1) !important; }
30 | .mb-1 { margin-block-end: var(--yoco-spacing-block-1) !important; }
31 | .my-1 { margin-block: var(--yoco-spacing-block-1) !important; }
32 | .ms-1 { margin-inline-start: var(--yoco-spacing-inline-1) !important; }
33 | .me-1 { margin-inline-end: var(--yoco-spacing-inline-1) !important; }
34 | .mx-1 { margin-inline: var(--yoco-spacing-inline-1) !important; }
35 |
36 | .m-2 { margin: var(--yoco-spacing-block-2) var(--yoco-spacing-inline-2) !important; }
37 | .mt-2 { margin-block-start: var(--yoco-spacing-block-2) !important; }
38 | .mb-2 { margin-block-end: var(--yoco-spacing-block-2) !important; }
39 | .my-2 { margin-block: var(--yoco-spacing-block-2) !important; }
40 | .ms-2 { margin-inline-start: var(--yoco-spacing-inline-2) !important; }
41 | .me-2 { margin-inline-end: var(--yoco-spacing-inline-2) !important; }
42 | .mx-2 { margin-inline: var(--yoco-spacing-inline-2) !important; }
43 |
44 | .m-3 { margin: var(--yoco-spacing-block-3) var(--yoco-spacing-inline-3) !important; }
45 | .mt-3 { margin-block-start: var(--yoco-spacing-block-3) !important; }
46 | .mb-3 { margin-block-end: var(--yoco-spacing-block-3) !important; }
47 | .my-3 { margin-block: var(--yoco-spacing-block-3) !important; }
48 | .ms-3 { margin-inline-start: var(--yoco-spacing-inline-3) !important; }
49 | .me-3 { margin-inline-end: var(--yoco-spacing-inline-3) !important; }
50 | .mx-3 { margin-inline: var(--yoco-spacing-inline-3) !important; }
51 |
52 | .p-0 { padding: 0 !important; }
53 | .pt-0 { padding-block-start: 0 !important; }
54 | .pb-0 { padding-block-end: 0 !important; }
55 | .py-0 { padding-block: 0 !important; }
56 | .ps-0 { padding-inline-start: 0 !important; }
57 | .pe-0 { padding-inline-end: 0 !important; }
58 | .px-0 { padding-inline: 0 !important; }
59 |
60 | .p-1 { padding: var(--yoco-spacing-block-1) var(--yoco-spacing-inline-1) !important; }
61 | .pt-1 { padding-block-start: var(--yoco-spacing-block-1) !important; }
62 | .pb-1 { padding-block-end: var(--yoco-spacing-block-1) !important; }
63 | .py-1 { padding-block: var(--yoco-spacing-block-1) !important; }
64 | .ps-1 { padding-inline-start: var(--yoco-spacing-inline-1) !important; }
65 | .pe-1 { padding-inline-end: var(--yoco-spacing-inline-1) !important; }
66 | .px-1 { padding-inline: var(--yoco-spacing-inline-1) !important; }
67 |
68 | .p-2 { padding: var(--yoco-spacing-block-2) var(--yoco-spacing-inline-2) !important; }
69 | .pt-2 { padding-block-start: var(--yoco-spacing-block-2) !important; }
70 | .pb-2 { padding-block-end: var(--yoco-spacing-block-2) !important; }
71 | .py-2 { padding-block: var(--yoco-spacing-block-2) !important; }
72 | .ps-2 { padding-inline-start: var(--yoco-spacing-inline-2) !important; }
73 | .pe-2 { padding-inline-end: var(--yoco-spacing-inline-2) !important; }
74 | .px-2 { padding-inline: var(--yoco-spacing-inline-2) !important; }
75 |
76 | .p-3 { padding: var(--yoco-spacing-block-3) var(--yoco-spacing-inline-3) !important; }
77 | .pt-3 { padding-block-start: var(--yoco-spacing-block-3) !important; }
78 | .pb-3 { padding-block-end: var(--yoco-spacing-block-3) !important; }
79 | .py-3 { padding-block: var(--yoco-spacing-block-3) !important; }
80 | .ps-3 { padding-inline-start: var(--yoco-spacing-inline-3) !important; }
81 | .pe-3 { padding-inline-end: var(--yoco-spacing-inline-3) !important; }
82 | .px-3 { padding-inline: var(--yoco-spacing-inline-3) !important; }
83 |
84 |
85 | .fw-n { font-weight: normal !important; }
86 |
87 |
88 | article > header > h6 {color: var(--pico-primary);}
89 |
--------------------------------------------------------------------------------
/docs/favicon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-152x152.png
--------------------------------------------------------------------------------
/docs/favicon-167x167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-167x167.png
--------------------------------------------------------------------------------
/docs/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/favicon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-180x180.png
--------------------------------------------------------------------------------
/docs/favicon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-192x192.png
--------------------------------------------------------------------------------
/docs/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/favicon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-48x48.png
--------------------------------------------------------------------------------
/docs/favicon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon-512x512.png
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yohn/PicoCSS/de779755c0574d313acf0cd7a52ff4cd7bd93955/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/js/FileValidator.js:
--------------------------------------------------------------------------------
1 | class FileValidator {
2 | /**
3 | * Initializes the FileValidator with a file input element.
4 | * @param {HTMLInputElement} fileInput - File input element to validate.
5 | * @param {boolean} displayList - Whether to display the file list (default: true).
6 | * @param {HTMLElement} customListContainer - Optional custom container for the file list.
7 | */
8 | constructor(fileInput, displayList = true, customListContainer = null) {
9 | if (!(fileInput instanceof HTMLInputElement) || fileInput.type !== "file") {
10 | throw new Error("FileValidator requires an input element of type 'file'.");
11 | }
12 |
13 | this.fileInput = fileInput;
14 | this.displayList = displayList;
15 | this.customListContainer = customListContainer;
16 |
17 | this.fileInput.addEventListener("change", () => this.validateFiles());
18 |
19 | // Create a container for the file list
20 | if (this.displayList) {
21 | this.fileListContainer = document.createElement("ul");
22 | this.fileListContainer.className = "file-list";
23 | if (this.customListContainer) {
24 | this.customListContainer.append(this.fileListContainer);
25 | } else {
26 | this.fileInput.insertAdjacentElement("afterend", this.fileListContainer);
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Validates files based on the accept and size attributes of the input element.
33 | * Displays a file list if files are valid.
34 | * @returns {boolean} - True if all files are valid, otherwise false.
35 | */
36 | validateFiles() {
37 | const accept = this.fileInput.getAttribute("accept") || "";
38 | const maxSize = parseInt(this.fileInput.getAttribute("data-max-size"), 10) || Infinity;
39 | const files = Array.from(this.fileInput.files);
40 |
41 | // Clear any existing list
42 | if (this.displayList) {
43 | this.fileListContainer.innerHTML = "";
44 | }
45 |
46 | if (files.length === 0) {
47 | console.log("No files selected.");
48 | return false;
49 | }
50 |
51 | for (const file of files) {
52 | if (accept && !this.isFileTypeValid(file, accept)) {
53 | console.error(`Invalid file type: ${file.name}`);
54 | this.fileInput.setCustomValidity(`Invalid file type: ${file.name}`);
55 | return false;
56 | }
57 |
58 | if (file.size > maxSize) {
59 | console.error(`File too large: ${file.name} (${file.size} bytes)`);
60 | this.fileInput.setCustomValidity(`File too large: ${file.name}`);
61 | return false;
62 | }
63 |
64 | // Add file to the list if valid
65 | if (this.displayList) {
66 | this.addFileToList(file);
67 | }
68 | }
69 |
70 | this.fileInput.setCustomValidity("");
71 | console.log("All files are valid.");
72 | return true;
73 | }
74 |
75 | /**
76 | * Adds a file to the displayed list, including an image preview if the file is an image.
77 | * @param {File} file - The file to add to the list.
78 | */
79 | addFileToList(file) {
80 | const listItem = document.createElement("li");
81 | listItem.textContent = `${file.name} (${(file.size / 1024).toFixed(2)} KB)`;
82 |
83 | // Add an image preview if the file is an image
84 | if (file.type.startsWith("image/")) {
85 | const imagePreview = document.createElement("img");
86 | imagePreview.src = URL.createObjectURL(file);
87 | imagePreview.style.maxWidth = "50px";
88 | imagePreview.style.maxHeight = "50px";
89 | imagePreview.onload = () => URL.revokeObjectURL(imagePreview.src); // Release memory
90 | listItem.prepend(imagePreview);
91 | }
92 |
93 | // Add a remove button
94 | const removeButton = document.createElement("button");
95 | removeButton.textContent = " ";
96 | removeButton.className = "btn-file-rm";
97 | removeButton.addEventListener("click", (event) => {
98 | event.stopPropagation(); // Prevent event from bubbling up to modal
99 | this.removeFile(file, listItem);
100 | });
101 |
102 | listItem.append(removeButton);
103 |
104 | if (this.customListContainer) {
105 | this.customListContainer.append(listItem);
106 | } else {
107 | this.fileListContainer.append(listItem);
108 | }
109 | }
110 |
111 | /**
112 | * Removes a file from the list and updates the input file element.
113 | * @param {File} file - The file to remove.
114 | * @param {HTMLElement} listItem - The list item element to remove.
115 | */
116 | removeFile(file, listItem) {
117 | const filesArray = Array.from(this.fileInput.files);
118 | const updatedFiles = filesArray.filter(f => f !== file);
119 |
120 | // Update the input element with the new FileList
121 | const dataTransfer = new DataTransfer();
122 | updatedFiles.forEach(f => dataTransfer.items.add(f));
123 | this.fileInput.files = dataTransfer.files;
124 |
125 | // Remove the list item from the display
126 | listItem.remove();
127 | }
128 |
129 | /**
130 | * Checks if a file's type matches any of the allowed types in the accept attribute.
131 | * @param {File} file - File object to check.
132 | * @param {string} accept - Accept attribute value (e.g., ".jpg, .png, image/*").
133 | * @returns {boolean} - True if the file type is valid, otherwise false.
134 | */
135 | isFileTypeValid(file, accept) {
136 | const acceptedTypes = accept.split(",").map(type => type.trim());
137 | return acceptedTypes.some(type => {
138 | if (type.includes("/") && file.type === type) return true;
139 | if (type.startsWith(".") && file.name.endsWith(type)) return true;
140 | if (type.endsWith("/*")) return file.type.startsWith(type.split("/")[0] + "/");
141 | return false;
142 | });
143 | }
144 | }
145 |
146 |
147 | // Usage example:
148 | //
149 | // const fileInput = document.getElementById("fileInput");
150 | // new FileValidator(fileInput, false); // Hide display list
151 | //
152 | // Alternatively, with custom list container:
153 | // const customListContainer = document.getElementById("customListContainer");
154 | // new FileValidator(fileInput, true, customListContainer);
155 |
156 | // Usage example2:
157 | //
158 | // 1048576 = 1MB
159 | // const checkfile = document.getElementById("checkfile");
160 | // new FileValidator(checkfile);
161 |
--------------------------------------------------------------------------------
/docs/js/Modal.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Modal
3 | *
4 | * Pico.css - https://picocss.com
5 | * Copyright 2019-2025 - Licensed under MIT
6 | * Modified by Yohn https://github.com/Yohn/PicoCSS
7 | */
8 | //document.addEventListener("DOMContentLoaded", () => {
9 | // Config
10 | const isOpenClass = "modal-is-open";
11 | const openingClass = "modal-is-opening";
12 | const closingClass = "modal-is-closing";
13 | const scrollbarWidthCssVar = "--pico-scrollbar-width";
14 | const animationDuration = 400; // ms
15 | let visibleModal = null;
16 |
17 | // Toggle modal
18 | const toggleModal = (event) => {
19 | event.preventDefault();
20 | const modal = document.getElementById(event.currentTarget.dataset.target);
21 | if (!modal) return;
22 | if(event.currentTarget.dataset.close) {
23 | const modalClose = document.getElementById(event.currentTarget.dataset.close);
24 | if(modalClose){
25 | closeModal(modalClose);
26 | setTimeout(() => modal && (modal.open ? closeModal(modal) : openModal(modal)), animationDuration);
27 | }
28 | } else {
29 | modal && (modal.open ? closeModal(modal) : openModal(modal));
30 | }
31 | };
32 |
33 | // Open modal
34 | const openModal = (modal) => {
35 | const { documentElement: html } = document;
36 | const scrollbarWidth = getScrollbarWidth();
37 | if (scrollbarWidth) {
38 | html.style.setProperty(scrollbarWidthCssVar, `${scrollbarWidth}px`);
39 | }
40 | html.classList.add(isOpenClass, openingClass);
41 | setTimeout(() => {
42 | visibleModal = modal;
43 | html.classList.remove(openingClass);
44 | }, animationDuration);
45 | modal.showModal();
46 | };
47 |
48 | // Close modal
49 | const closeModal = (modal) => {
50 | visibleModal = null;
51 | const { documentElement: html } = document;
52 | html.classList.add(closingClass);
53 | setTimeout(() => {
54 | html.classList.remove(closingClass, isOpenClass);
55 | html.style.removeProperty(scrollbarWidthCssVar);
56 | modal.close();
57 | }, animationDuration);
58 | };
59 |
60 | // Close with a click outside
61 | document.addEventListener("click", (event) => {
62 | if (visibleModal === null) return;
63 | const modalContent = visibleModal.querySelector("article");
64 | const isClickInside = modalContent.contains(event.target);
65 | !isClickInside && closeModal(visibleModal);
66 | });
67 |
68 | // Close with Esc key
69 | document.addEventListener("keydown", (event) => {
70 | if (event.key === "Escape" && visibleModal) {
71 | closeModal(visibleModal);
72 | }
73 | });
74 |
75 | // Get scrollbar width
76 | const getScrollbarWidth = () => {
77 | const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
78 | return scrollbarWidth;
79 | };
80 |
81 | // Is scrollbar visible
82 | const isScrollbarVisible = () => {
83 | return document.body.scrollHeight > screen.height;
84 | };
85 | //})
86 |
--------------------------------------------------------------------------------
/docs/js/PicoTabs.js:
--------------------------------------------------------------------------------
1 | /*
2 | // To not have any keypresses change the tabs use the following
3 | document.addEventListener("DOMContentLoaded", () => {
4 | const tabs = document.querySelectorAll('[role="tab"]');
5 | const panels = document.querySelectorAll('[role="tabpanel"]');
6 |
7 | tabs.forEach((tab, index) => {
8 | tab.addEventListener("click", () => {
9 | // Reset all tabs and panels
10 | tabs.forEach((t, i) => {
11 | t.setAttribute("aria-selected", "false");
12 | t.setAttribute("tabindex", "-1");
13 | panels[i].setAttribute("hidden", true);
14 | });
15 |
16 | // Activate the clicked tab
17 | tab.setAttribute("aria-selected", "true");
18 | tab.setAttribute("tabindex", "0");
19 | document
20 | .querySelector('[aria-labelledby="' + tab.id + '"]')
21 | .removeAttribute("hidden");
22 |
23 | // Focus the activated tab
24 | tab.focus();
25 | });
26 | });
27 | });
28 | */
29 |
30 | class PicoTabs {
31 | constructor(tabListContainerSelector) {
32 | this.tabLists = document.querySelectorAll(tabListContainerSelector);
33 |
34 | // Proceed only if tablists are found
35 | if (this.tabLists.length === 0) {
36 | console.warn(`No elements with ${tabListContainerSelector} found on the page.`);
37 | return;
38 | }
39 |
40 | this.tabLists.forEach((tabList) => {
41 | const tabs = Array.from(tabList.querySelectorAll('[role="tab"]'));
42 | const panels = Array.from(tabList.querySelectorAll('[role="tabpanel"]'));
43 |
44 | // Filter out nested tabs and panels
45 | const rootTabs = tabs.filter((tab) => tab.closest(tabListContainerSelector) === tabList);
46 | const rootPanels = panels.filter((panel) => panel.closest(tabListContainerSelector) === tabList);
47 |
48 | // Proceed only if root tabs and panels are found
49 | if (rootTabs.length === 0 || rootPanels.length === 0) {
50 | console.warn("No root tabs or panels found in a tablist, skipping initialization.");
51 | return;
52 | }
53 |
54 | this.init(tabList, rootTabs, rootPanels);
55 | });
56 | }
57 |
58 | init(tabList, tabs, panels) {
59 | tabs.forEach((tab, index) => {
60 | tab.addEventListener("click", () => this.activateTab(tabs, panels, index));
61 | tab.addEventListener("keydown", (e) => this.handleKeyDown(e, tabs, panels, index));
62 | });
63 | }
64 |
65 | // Activate a tab and corresponding panel
66 | activateTab(tabs, panels, index) {
67 | // Reset all tabs and panels within the current tablist
68 | tabs.forEach((tab, i) => {
69 | tab.setAttribute("aria-selected", "false");
70 | tab.setAttribute("tabindex", "-1");
71 | panels[i].setAttribute("hidden", "true");
72 | });
73 |
74 | // Activate the specified tab
75 | tabs[index].setAttribute("aria-selected", "true");
76 | tabs[index].setAttribute("tabindex", "0");
77 | panels[index].removeAttribute("hidden");
78 |
79 | // Focus the activated tab
80 | tabs[index].focus();
81 | }
82 |
83 | // Handle keyboard navigation
84 | handleKeyDown(event, tabs, panels, currentIndex) {
85 | switch (event.key) {
86 | case "ArrowLeft":
87 | event.preventDefault();
88 | this.activateTab(tabs, panels, (currentIndex - 1 + tabs.length) % tabs.length);
89 | break;
90 | case "ArrowRight":
91 | event.preventDefault();
92 | this.activateTab(tabs, panels, (currentIndex + 1) % tabs.length);
93 | break;
94 | case "Home":
95 | event.preventDefault();
96 | this.activateTab(tabs, panels, 0);
97 | break;
98 | case "End":
99 | event.preventDefault();
100 | this.activateTab(tabs, panels, tabs.length - 1);
101 | break;
102 | default:
103 | break;
104 | }
105 | }
106 | }
107 |
108 | //document.addEventListener("DOMContentLoaded", () => {
109 | // new PicoTabs('[role="tablist"]');
110 | //});
111 |
--------------------------------------------------------------------------------
/docs/js/SwitchColorMode.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Minimal theme switcher using a checkbox
3 | *
4 | * Pico.css - https://picocss.com
5 | * Copyright 2019-2025 - Licensed under MIT
6 | * Modified by Yohn https://github.com/Yohn/PicoCSS
7 | */
8 |
9 | const SwitchColorMode = {
10 | // Config
11 | _scheme: "auto",
12 | toggleSelector: "input[name='color-mode-toggle']",
13 | rootAttribute: "data-theme",
14 | localStorageKey: "picoPreferredColorScheme",
15 |
16 | // Init
17 | init() {
18 | this.checkbox = document.querySelector(this.toggleSelector);
19 | if (!this.checkbox) {
20 | console.error("Theme switcher toggle not found");
21 | return;
22 | }
23 |
24 | // If first visit, use the theme from attribute; otherwise, use stored preference
25 | this.scheme = this.schemeFromLocalStorage ?? this.schemeFromHTML;
26 |
27 | // Set checkbox state based on the applied theme
28 | this.checkbox.checked = this.scheme === "dark";
29 |
30 | // Listen for user changes
31 | this.checkbox.addEventListener("change", () => {
32 | this.scheme = this.checkbox.checked ? "dark" : "light";
33 | this.schemeToLocalStorage();
34 | });
35 | },
36 |
37 | // Get color scheme from local storage
38 | get schemeFromLocalStorage() {
39 | return window.localStorage?.getItem(this.localStorageKey);
40 | },
41 |
42 | // Get the default theme from the attribute
43 | get schemeFromHTML() {
44 | return document.documentElement.getAttribute(this.rootAttribute) ?? "auto";
45 | },
46 |
47 | // Preferred color scheme from system
48 | get preferredColorScheme() {
49 | return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
50 | },
51 |
52 | // Set scheme
53 | set scheme(scheme) {
54 | if (scheme === "auto") {
55 | this._scheme = this.preferredColorScheme;
56 | } else if (scheme === "dark" || scheme === "light") {
57 | this._scheme = scheme;
58 | }
59 | this.applyScheme();
60 | },
61 |
62 | // Get scheme
63 | get scheme() {
64 | return this._scheme;
65 | },
66 |
67 | // Apply scheme
68 | applyScheme() {
69 | document.documentElement.setAttribute(this.rootAttribute, this._scheme);
70 | },
71 |
72 | // Store scheme to local storage
73 | schemeToLocalStorage() {
74 | window.localStorage?.setItem(this.localStorageKey, this.scheme);
75 | },
76 | };
77 |
78 | // Init
79 | SwitchColorMode.init();
--------------------------------------------------------------------------------
/docs/js/old/MinimalThemeSwitcher.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Minimal theme switcher
3 | *
4 | * Pico.css - https://picocss.com
5 | * Copyright 2019-2024 - Licensed under MIT
6 | */
7 |
8 | const themeSwitcher = {
9 | // Config
10 | _scheme: "auto",
11 | menuTarget: "details.dropdown",
12 | buttonsTarget: "a[data-theme-switcher]",
13 | buttonAttribute: "data-theme-switcher",
14 | rootAttribute: "data-theme",
15 | localStorageKey: "picoPreferredColorScheme",
16 |
17 | // Init
18 | init() {
19 | this.scheme = this.schemeFromLocalStorage;
20 | this.initSwitchers();
21 | },
22 |
23 | // Get color scheme from local storage
24 | get schemeFromLocalStorage() {
25 | return window.localStorage?.getItem(this.localStorageKey) ?? this._scheme;
26 | },
27 |
28 | // Preferred color scheme
29 | get preferredColorScheme() {
30 | return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
31 | },
32 |
33 | // Init switchers
34 | initSwitchers() {
35 | const buttons = document.querySelectorAll(this.buttonsTarget);
36 | buttons.forEach((button) => {
37 | button.addEventListener(
38 | "click",
39 | (event) => {
40 | event.preventDefault();
41 | // Set scheme
42 | this.scheme = button.getAttribute(this.buttonAttribute);
43 | // Close dropdown
44 | document.querySelector(this.menuTarget)?.removeAttribute("open");
45 | },
46 | false
47 | );
48 | });
49 | },
50 |
51 | // Set scheme
52 | set scheme(scheme) {
53 | if (scheme == "auto") {
54 | this._scheme = this.preferredColorScheme;
55 | } else if (scheme == "dark" || scheme == "light") {
56 | this._scheme = scheme;
57 | }
58 | this.applyScheme();
59 | this.schemeToLocalStorage();
60 | },
61 |
62 | // Get scheme
63 | get scheme() {
64 | return this._scheme;
65 | },
66 |
67 | // Apply scheme
68 | applyScheme() {
69 | document.querySelector("html")?.setAttribute(this.rootAttribute, this.scheme);
70 | },
71 |
72 | // Store scheme to local storage
73 | schemeToLocalStorage() {
74 | window.localStorage?.setItem(this.localStorageKey, this.scheme);
75 | },
76 | };
77 |
78 | // Init
79 | themeSwitcher.init();
80 |
--------------------------------------------------------------------------------
/docs/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Pico CSS",
3 | "name": "Pico CSS",
4 | "description": "A pure HTML example, without dependencies.",
5 | "icons": [
6 | {
7 | "src": "favicon.ico",
8 | "sizes": "64x64 32x32 24x24 16x16",
9 | "type": "image/x-icon"
10 | },
11 | {
12 | "src": "favicon-48x48.png",
13 | "type": "image/png",
14 | "sizes": "48x48"
15 | },
16 | {
17 | "src": "favicon-192x192.png",
18 | "type": "image/png",
19 | "sizes": "192x192"
20 | },
21 | {
22 | "src": "favicon-512x512.png",
23 | "type": "image/png",
24 | "sizes": "512x512"
25 | }
26 | ],
27 | "start_url": ".",
28 | "display": "standalone",
29 | "theme_color": "#000000",
30 | "background_color": "#ffffff"
31 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@yohns/picocss",
3 | "version": "2.2.10",
4 | "description": "Minimal CSS Framework for semantic HTML, updated with enhanced capabilities.",
5 | "authors": [
6 | {
7 | "name": "Lucas Larroche",
8 | "website": "https://github.com/picocss/pico"
9 | },
10 | {
11 | "name": "John Brittain III",
12 | "website": "https://github.com/Yohn/PicoCSS"
13 | }
14 | ],
15 | "main": "css/pico.min.css",
16 | "homepage": "https://picocss.com",
17 | "license": "MIT",
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/Yohn/PicoCSS.git"
21 | },
22 | "keywords": [
23 | "css",
24 | "css-framework",
25 | "dark-mode",
26 | "dark-theme",
27 | "lightweight",
28 | "minimal",
29 | "minimalist",
30 | "minimalistic",
31 | "native-html",
32 | "scss-framework",
33 | "semantic"
34 | ],
35 | "bugs": {
36 | "url": "https://github.com/Yohn/PicoCSS/issues"
37 | },
38 | "scripts": {
39 | "✨": "run-s build",
40 | "build": "run-s start lint \"build:*\" done --silent",
41 | "build-dev": "run-s start lint \"build-dev:*\" done --silent",
42 | "dev": "nodemon -q --watch scss/ --ext scss --exec \"run-s build-dev\"",
43 | "lint": "run-s \"lint:*\" --silent",
44 | "lint:prettier": "prettier --write --log-level error \"scss/**/*.scss\"",
45 | "lint:sort-scss": "postcss --config scss ./scss/**/*.scss --replace",
46 | "build-dev:css": "sass --no-source-map --style expanded --no-error-css scss/:css/",
47 | "build:css": "sass --no-source-map --style expanded --no-error-css scss/:css/",
48 | "build-dev:themes": "node scripts/build-dev",
49 | "build:themes": "node scripts/build-themes",
50 | "build-dev:autoprefix": "postcss --config css --replace css/*.css !css/*.min.css",
51 | "build:autoprefix": "postcss --config css --replace css/*.css !css/*.min.css",
52 | "build-dev:minify": "cleancss -O1 --with-rebase --batch --batch-suffix .min css/*.css !css/*.min.css",
53 | "build:minify": "cleancss -O1 --with-rebase --batch --batch-suffix .min css/*.css !css/*.min.css",
54 | "build-dev:postbuild": "node scripts/copy-docs-css-files",
55 | "build:postbuild": "node scripts/copy-docs-css-files",
56 | "prelint": "echo '[@Yohns/PicoCSS] ✨ Lint'",
57 | "prebuild:css": "echo '[@Yohns/PicoCSS] ✨ Compile'",
58 | "prebuild:themes": "echo '[@Yohns/PicoCSS] ✨ Compile themes'",
59 | "prebuild:autoprefix": "echo '[@Yohns/PicoCSS] ✨ Autoprefix'",
60 | "prebuild:minify": "echo '[@Yohns/PicoCSS] ✨ Minify'",
61 | "start": "echo '\\033[96m[@Yohns/PicoCSS] ✨ Start\\033[0m'",
62 | "done": "echo '\\033[32m[@Yohns/PicoCSS] ✨ Done\\033[0m'"
63 | },
64 | "devDependencies": {
65 | "autoprefixer": "^10.4.20",
66 | "caniuse-lite": "^1.0.30001687",
67 | "clean-css-cli": "^5.6.3",
68 | "css-declaration-sorter": "^7.2.0",
69 | "nodemon": "^3.1.7",
70 | "npm-run-all": "^4.1.5",
71 | "postcss": "^8.4.49",
72 | "postcss-cli": "^11.0.0",
73 | "postcss-scss": "^4.0.9",
74 | "prettier": "^3.4.2",
75 | "sass": "^1.83.4"
76 | },
77 | "engines": {
78 | "node": ">=20"
79 | },
80 | "browserslist": [
81 | "defaults"
82 | ]
83 | }
84 |
--------------------------------------------------------------------------------
/scripts/build-dev.js:
--------------------------------------------------------------------------------
1 | const sass = require("sass");
2 | const path = require("path");
3 | const fs = require("fs");
4 |
5 | const themeColors = [
6 | //? uncomment 1 theme you'd like to build for the dev environment.
7 | //"amber",
8 | //"blue",
9 | //"cyan",
10 | //"fuchsia",
11 | //"green",
12 | //"grey",
13 | //"indigo",
14 | "indigo",
15 | //"lime",
16 | //"orange",
17 | //"pink",
18 | //"pumpkin",
19 | //"purple",
20 | //"red",
21 | //"sand",
22 | //"slate",
23 | //"violet",
24 | //"yellow",
25 | //"zinc",
26 | ];
27 |
28 | const tempScssFoldername = path.join(__dirname, "../.pico");
29 | const cssFoldername = path.join(__dirname, "../css");
30 |
31 | // Create a folder if it doesn't exist
32 | const createFolderIfNotExists = (foldername) => {
33 | if (!fs.existsSync(foldername)) {
34 | fs.mkdirSync(foldername);
35 | }
36 | };
37 |
38 | // Empty a folder
39 | const emptyFolder = (foldername) => {
40 | // Delete all files in the temp folder
41 | fs.readdirSync(foldername).forEach((file) => {
42 | fs.unlinkSync(path.join(foldername, file));
43 | });
44 | };
45 |
46 | // Create the temp folder if it doesn't exist
47 | createFolderIfNotExists(tempScssFoldername);
48 |
49 | // Empty the temp folder
50 | emptyFolder(tempScssFoldername);
51 |
52 | // Loop through the theme colors
53 | themeColors.forEach((themeColor, colorIndex) => {
54 | // All the versions to generate
55 | const versions = [
56 | {
57 | name: "pico",
58 | content: `@use "../scss" with (
59 | $theme-color: "${themeColor}"
60 | );`,
61 | },
62 | //{
63 | // name: "pico.classless",
64 | // content: `@use "../scss" with (
65 | // $theme-color: "${themeColor}",
66 | // $enable-semantic-container: true,
67 | // $enable-classes: false
68 | // );`,
69 | //},
70 | //{
71 | // name: "pico.fluid.classless",
72 | // content: `@use "../scss" with (
73 | // $theme-color: "${themeColor}",
74 | // $enable-semantic-container: true,
75 | // $enable-viewport: false,
76 | // $enable-classes: false
77 | // );`,
78 | //},
79 | //{
80 | // name: "pico.conditional",
81 | // content: `@use "../scss" with (
82 | // $theme-color: "${themeColor}",
83 | // $parent-selector: ".pico"
84 | // );`,
85 | //},
86 | //{
87 | // name: "pico.classless.conditional",
88 | // content: `@use "../scss" with (
89 | // $theme-color: "${themeColor}",
90 | // $enable-semantic-container: true,
91 | // $enable-classes: false,
92 | // $parent-selector: ".pico"
93 | // );`,
94 | //},
95 | //{
96 | // name: "pico.fluid.classless.conditional",
97 | // content: `@use "../scss" with (
98 | // $theme-color: "${themeColor}",
99 | // $enable-semantic-container: true,
100 | // $enable-viewport: false,
101 | // $enable-classes: false,
102 | // $parent-selector: ".pico"
103 | // );`,
104 | //},
105 | ];
106 |
107 | const displayAsciiProgress = ({length, index, color}) => {
108 | const progress = Math.round((index / length) * 100);
109 | const bar = "■".repeat(progress / 10);
110 | const empty = "□".repeat(10 - progress / 10);
111 | process.stdout.write(`[@picocss/pico] ✨ ${bar}${empty} ${color}\r`);
112 | };
113 |
114 | // Loop through the versions
115 | versions.forEach((version) => {
116 | displayAsciiProgress({
117 | length: themeColors.length,
118 | index: colorIndex,
119 | color: themeColor.charAt(0).toUpperCase() + themeColor.slice(1),
120 | });
121 |
122 | // Create the file
123 | fs.writeFileSync(
124 | path.join(tempScssFoldername, `${version.name}.${themeColor}.scss`),
125 | version.content,
126 | );
127 |
128 | // Compile the file
129 | const result = sass.compile(
130 | path.join(tempScssFoldername, `${version.name}.${themeColor}.scss`),
131 | { outputStyle: "compressed" },
132 | );
133 |
134 | // Write the file
135 | fs.writeFileSync(path.join(cssFoldername, `${version.name}.${themeColor}.css`), result.css);
136 |
137 | // Clear the console - only if running in a TTY
138 | if (process.stdout.isTTY) {
139 | process.stdout.clearLine();
140 | process.stdout.cursorTo(0);
141 | }
142 | });
143 | });
144 |
145 | // Empty the temp folder
146 | emptyFolder(tempScssFoldername);
147 |
--------------------------------------------------------------------------------
/scripts/build-themes.js:
--------------------------------------------------------------------------------
1 | const sass = require("sass");
2 | const path = require("path");
3 | const fs = require("fs");
4 |
5 | const themeColors = [
6 | "amber",
7 | "azure",
8 | "blue",
9 | "cyan",
10 | "fuchsia",
11 | "green",
12 | "grey",
13 | "indigo",
14 | "jade",
15 | "lime",
16 | "orange",
17 | "pink",
18 | "pumpkin",
19 | "purple",
20 | "red",
21 | "sand",
22 | "slate",
23 | "violet",
24 | "yellow",
25 | "zinc",
26 | ];
27 |
28 | const tempScssFoldername = path.join(__dirname, "../.pico");
29 | const cssFoldername = path.join(__dirname, "../css");
30 |
31 | // Create a folder if it doesn't exist
32 | const createFolderIfNotExists = (foldername) => {
33 | if (!fs.existsSync(foldername)) {
34 | fs.mkdirSync(foldername);
35 | }
36 | };
37 |
38 | // Empty a folder
39 | const emptyFolder = (foldername) => {
40 | // Delete all files in the temp folder
41 | fs.readdirSync(foldername).forEach((file) => {
42 | fs.unlinkSync(path.join(foldername, file));
43 | });
44 | };
45 |
46 | // Create the temp folder if it doesn't exist
47 | createFolderIfNotExists(tempScssFoldername);
48 |
49 | // Empty the temp folder
50 | emptyFolder(tempScssFoldername);
51 |
52 | // Loop through the theme colors
53 | themeColors.forEach((themeColor, colorIndex) => {
54 | // All the versions to generate
55 | const versions = [
56 | {
57 | name: "pico",
58 | content: `@use "../scss" with (
59 | $theme-color: "${themeColor}"
60 | );`,
61 | },
62 | {
63 | name: "pico.classless",
64 | content: `@use "../scss" with (
65 | $theme-color: "${themeColor}",
66 | $enable-semantic-container: true,
67 | $enable-classes: false
68 | );`,
69 | },
70 | {
71 | name: "pico.fluid.classless",
72 | content: `@use "../scss" with (
73 | $theme-color: "${themeColor}",
74 | $enable-semantic-container: true,
75 | $enable-viewport: false,
76 | $enable-classes: false
77 | );`,
78 | },
79 | {
80 | name: "pico.conditional",
81 | content: `@use "../scss" with (
82 | $theme-color: "${themeColor}",
83 | $parent-selector: ".pico"
84 | );`,
85 | },
86 | {
87 | name: "pico.classless.conditional",
88 | content: `@use "../scss" with (
89 | $theme-color: "${themeColor}",
90 | $enable-semantic-container: true,
91 | $enable-classes: false,
92 | $parent-selector: ".pico"
93 | );`,
94 | },
95 | {
96 | name: "pico.fluid.classless.conditional",
97 | content: `@use "../scss" with (
98 | $theme-color: "${themeColor}",
99 | $enable-semantic-container: true,
100 | $enable-viewport: false,
101 | $enable-classes: false,
102 | $parent-selector: ".pico"
103 | );`,
104 | },
105 | ];
106 |
107 | const displayAsciiProgress = ({length, index, color}) => {
108 | const progress = Math.round((index / length) * 100);
109 | const bar = "■".repeat(progress / 10);
110 | const empty = "□".repeat(10 - progress / 10);
111 | process.stdout.write(`[@Yohn/PicoCSS] ✨ ${bar}${empty} ${color}\r`);
112 | };
113 |
114 | // Loop through the versions
115 | versions.forEach((version) => {
116 | displayAsciiProgress({
117 | length: themeColors.length,
118 | index: colorIndex,
119 | color: themeColor.charAt(0).toUpperCase() + themeColor.slice(1),
120 | });
121 |
122 | // Create the file
123 | fs.writeFileSync(
124 | path.join(tempScssFoldername, `${version.name}.${themeColor}.scss`),
125 | version.content,
126 | );
127 |
128 | // Compile the file
129 | const result = sass.compile(
130 | path.join(tempScssFoldername, `${version.name}.${themeColor}.scss`),
131 | { outputStyle: "compressed" },
132 | );
133 |
134 | // Write the file
135 | fs.writeFileSync(path.join(cssFoldername, `${version.name}.${themeColor}.css`), result.css);
136 |
137 | // Clear the console - only if running in a TTY
138 | if (process.stdout.isTTY) {
139 | process.stdout.clearLine();
140 | process.stdout.cursorTo(0);
141 | }
142 | });
143 | });
144 |
145 | // Empty the temp folder
146 | emptyFolder(tempScssFoldername);
147 |
--------------------------------------------------------------------------------
/scripts/copy-docs-css-files-dev.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // Define the source and destination paths
5 | const filesToCopy = [
6 | { src: 'css/pico.indigo.css', dest: 'docs/pico.css' },
7 | { src: 'css/pico.indigo.min.css', dest: 'docs/pico.min.css' },
8 | { src: 'css/pico.colors.min.css', dest: 'docs/pico.colors.min.css' }
9 | ];
10 |
11 | // Copy each file to the destination
12 | filesToCopy.forEach(file => {
13 | fs.copyFileSync(file.src, file.dest);
14 | console.log(`[@Yohns/PicoCSS] ✨ Copied new file to ${file.dest}`);
15 | });
16 |
--------------------------------------------------------------------------------
/scripts/copy-docs-css-files.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // Define the source and destination paths
5 | const filesToCopy = [
6 | { src: 'css/pico.css', dest: 'docs/pico.css' },
7 | { src: 'css/pico.min.css', dest: 'docs/pico.min.css' },
8 | { src: 'css/pico.colors.min.css', dest: 'docs/pico.colors.min.css' }
9 | ];
10 |
11 | // Copy each file to the destination
12 | filesToCopy.forEach(file => {
13 | fs.copyFileSync(file.src, file.dest);
14 | console.log(`[@Yohns/PicoCSS] ✨ Copied new file to ${file.dest}`);
15 | });
16 |
--------------------------------------------------------------------------------
/scss/_index.scss:
--------------------------------------------------------------------------------
1 | @use "helpers/copyright";
2 |
3 | // Config
4 | @forward "settings";
5 |
6 | // Theming
7 | @use "themes/default";
8 |
9 | // Layout
10 | @use "layout/document"; // html
11 | @use "layout/landmarks"; // body, header, main, footer
12 | @use "layout/section"; // section
13 | @use "layout/container"; // .container, .container-fluid
14 | @use "layout/grid"; // .grid
15 | @use "layout/row"; // .row, .row-fluid, .offset-*, .span-*
16 | @use "layout/overflow-auto"; // .overflow-auto
17 |
18 | // Content
19 | @use "content/typography"; // headings, p, ul, blockquote, ...
20 | @use "content/link"; // a, role="link"
21 | @use "content/button"; // button, role="button", type="button", type="submit" ...
22 | @use "content/table"; // table, tr, td, ...
23 | @use "content/embedded"; // audio, canvas, iframe, img, svg, video
24 | @use "content/code"; // pre, code, ...
25 | @use "content/figure"; // figure, figcaption
26 | @use "content/misc"; // hr, template, [hidden], dialog, canvas
27 |
28 | // Forms
29 | @use "forms/basics"; // input, select, textarea, label, fieldset, legend
30 | @use "forms/checkbox-radio-switch"; // type="checkbox", type="radio", role="switch"
31 | @use "forms/input-color"; // type="color"
32 | @use "forms/input-date"; // type="date", type="datetime-local", type="month", type="time", type="week"
33 | @use "forms/input-file"; // type="file"
34 | @use "forms/input-range"; // type="range"
35 | @use "forms/input-search"; // type="search"
36 | @use "forms/validation"; // validation for all form elements except select[multiple],select[size], input:not([type="button"], [type="reset"], [type="image"], [type="submit"], [type="checkbox"], [type="radio"]
37 | @use "forms/floating"; // floating labels
38 |
39 | // Components
40 | @use "components/accordion"; // details, summary
41 | @use "components/card"; // article, role="article"
42 | @use "components/dropdown"; // details.dropdown
43 | @use "components/group"; // role="group"
44 | @use "components/loading"; // aria-busy=true
45 | @use "components/modal"; // dialog
46 | @use "components/nav"; // nav
47 | @use "components/nav-hamburger"; // role="navigation" for hamburger-menu for the nav component
48 | @use "components/progress"; // progress
49 | @use "components/tooltip"; // data-tooltip
50 | @use "components/tab"; // [role="tablist"] // classless tabs
51 | @use "components/tab-region"; // section[role="region"] // tabs
52 | @use "components/popover"; // dialog[role="alert"]
53 | // V3 Remove notification in favor of popovers..
54 | @use "components/notification"; // dialog[role="alert"]
55 | @use "components/timeline"; // addition, kind of out of scope but wanted to add
56 |
57 | // Utilities
58 | @use "utilities/accessibility"; // -ms-touch-action, aria-*
59 | @use "utilities/reduce-motion"; // prefers-reduced-motion
60 |
--------------------------------------------------------------------------------
/scss/_settings.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "sass:math";
3 | @use "sass:list";
4 |
5 | // Settings
6 | // ––––––––––––––––––––
7 |
8 | // Theme colors
9 | $yo-themes: (
10 | "amber",
11 | "azure",
12 | "blue",
13 | "cyan",
14 | "fuchsia",
15 | "green",
16 | "indigo",
17 | "jade",
18 | "lime",
19 | "orange",
20 | "pink",
21 | "pumpkin",
22 | "purple",
23 | "red",
24 | "violet",
25 | "yellow"
26 | );
27 | $random: list.nth($yo-themes, math.random(list.length($yo-themes)));
28 | // picks a random theme to use as default
29 | $theme-color: $random !default; // amber, azure, blue, cyan, fuchsia, green, grey, indigo, jade, lime, orange, pink, pumpkin, purple, red, sand, slate, violet, yellow, zinc
30 |
31 | // Prefix for CSS variables
32 | $css-var-prefix: "--pico-" !default; // Must start with "--"
33 |
34 | // Define the root element used to target , ,
35 | // with $enable-semantic-container and $enable-responsive-spacings
36 | $semantic-root-element: "body" !default;
37 |
38 | // Enable , , inside $semantic-root-element as containers
39 | $enable-semantic-container: false !default;
40 |
41 | // Enable a centered viewport for , , inside $semantic-root-element
42 | // Fluid layout if disabled
43 | $enable-viewport: true !default;
44 |
45 | // Enable responsive spacings for , , , ,
46 | // Fixed spacings by default
47 | $enable-responsive-spacings: false !default;
48 |
49 | // Enable responsive typography
50 | // Fixed root element size (rem) if disabled
51 | $enable-responsive-typography: true !default;
52 |
53 | // Enable .classes
54 | // .classless version if disabled
55 | $enable-classes: true !default;
56 |
57 | // Enable transitions
58 | $enable-transitions: true !default;
59 |
60 | // Enable overriding with !important
61 | $enable-important: true !default;
62 |
63 | // Optional parent selector
64 | // If defined, all HTML tags are wrapped with this selector
65 | // :root is not wrapped
66 | $parent-selector: "" !default;
67 |
68 | $row-columns: 12 !default;
69 |
70 | // Breakpoints, viewports and root font size
71 | $breakpoints: () !default;
72 | $breakpoints: map.deep-merge(
73 | (
74 | // Small (landscape phones)
75 | // Font size: 17px
76 | sm: (
77 | breakpoint: 576px,
78 | viewport: 510px,
79 | root-font-size: 106.25%,
80 | ),
81 |
82 | // Medium (tablets)
83 | // Font size: 18px
84 | md: (
85 | breakpoint: 768px,
86 | viewport: 700px,
87 | root-font-size: 112.5%,
88 | ),
89 |
90 | // Large
91 | // Font size: 19px
92 | lg: (
93 | breakpoint: 1024px,
94 | viewport: 950px,
95 | root-font-size: 118.75%,
96 | ),
97 |
98 | // Extra large
99 | // Font size: 20px
100 | xl: (
101 | breakpoint: 1280px,
102 | viewport: 1200px,
103 | root-font-size: 125%,
104 | ),
105 |
106 | // Extra extra large
107 | // Font size: 21px
108 | xxl: (
109 | breakpoint: 1536px,
110 | viewport: 1450px,
111 | root-font-size: 131.25%,
112 | )
113 | ),
114 | $breakpoints
115 | );
116 |
117 | // Modules to export
118 | $modules: () !default;
119 | $modules: map.merge(
120 | (
121 | // Theme
122 | "themes/default": true,
123 |
124 | // Layout
125 | "layout/document": true,
126 | "layout/landmarks": true,
127 | "layout/container": true,
128 | "layout/section": true,
129 |
130 | // V3 Notes
131 | // Add to layout folder, and have it auto build the a grid system
132 | // will add notes someplace about idea on how to enable.
133 | // use grid-template
134 | // maybe only then keep the grid and remove the row system.
135 | "layout/grid": true,
136 | "layout/row": true,
137 | "layout/overflow-auto": true,
138 |
139 | // Content
140 | "content/link": true,
141 | "content/typography": true,
142 | "content/embedded": true,
143 | "content/button": true,
144 | "content/table": true,
145 | "content/code": true,
146 | "content/figure": true,
147 | "content/misc": true,
148 |
149 | // Forms
150 | "forms/basics": true,
151 | "forms/checkbox-radio-switch": true,
152 | "forms/input-color": true,
153 | "forms/input-date": true,
154 | "forms/input-file": true,
155 | "forms/input-range": true,
156 | "forms/input-search": true,
157 | "forms/floating": true,
158 | // V3 Notes
159 | // Remove original pico validation in favor of :user-[in]valid
160 | "forms/validation": true,
161 |
162 | // Components
163 | "components/accordion": true,
164 | "components/card": true,
165 | // V3 Notes
166 | // Allow for sub-menus in dropdown
167 | "components/dropdown": true,
168 | "components/group": true,
169 | "components/loading": true,
170 | "components/modal": true,
171 | // V3 Notes
172 | // incorperate the tag into navigations.
173 | "components/nav": true,
174 | "components/progress": true,
175 | "components/tab": true,
176 |
177 | // [role=tablist]
178 | // I wish role="tablist|tab|tabpanel" could be used on and tag.
179 | "components/tab-region": false,
180 | // DOES NOT WORK IN CHROME
181 | "components/tooltip": true,
182 | // V3 Notes
183 | // might find a new way to do this, I'll wait to hear feedback on this approach.
184 | "components/nav-hamburger": true,
185 |
186 | // V3 Notes
187 | // Notification should be redone to be more in line with Pico's design
188 | "components/notification": false,
189 | "components/popover": true,
190 | // V3 Notes
191 | // timeline disabled by default because its more out of the scope of this project,
192 | // and should be moved to a separate "extras" repository, to be included.
193 | "components/timeline": true,
194 |
195 | // Utilities
196 | "utilities/accessibility": true,
197 | "utilities/reduce-motion": true
198 | ),
199 | $modules
200 | );
201 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_background-colors.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "sass:list";
3 | @use "../../colors";
4 | @use "settings";
5 | @use "utils";
6 |
7 | $enable-css-vars: map.get(settings.$utilities, "css-vars");
8 | $background-color-property-name: map.get(settings.$properties, "background-color");
9 | $color-property-name: map.get(settings.$properties, "color");
10 | $css-var-color-prefix: #{settings.$css-var-prefix}#{$color-property-name};
11 |
12 | @mixin foreground-color($background-color) {
13 | @if map.get(settings.$utilities, "color-for-background-colors") {
14 | @if utils.foreground-brightness($background-color) == "light" {
15 | @if $enable-css-vars {
16 | color: var(#{$css-var-color-prefix}-light);
17 | } @else {
18 | color: utils.display-color(map.get(settings.$palette, "light-color"));
19 | }
20 | } @else {
21 | @if $enable-css-vars {
22 | color: var(#{$css-var-color-prefix}-dark);
23 | } @else {
24 | color: utils.display-color(map.get(settings.$palette, "dark-color"));
25 | }
26 | }
27 | }
28 | }
29 |
30 | @mixin background-colors {
31 | @if map.get(settings.$utilities, "background-colors") {
32 | // Loop through color families
33 | @each $family, $colors in colors.$colors {
34 | @if list.index(map.get(settings.$palette, "color-families"), $family) {
35 | $css-var-family-name: #{$css-var-color-prefix}-#{$family};
36 | $class-family-name: #{$background-color-property-name}-#{$family};
37 |
38 | // Loop through colors
39 | @each $shade, $color-value in $colors {
40 | // Main color
41 | @if $shade == "main" and map.get(settings.$palette, "enable-main-color") {
42 | $value: $color-value;
43 | @if $enable-css-vars {
44 | $value: var(#{$css-var-family-name});
45 | } @else {
46 | $value: utils.display-color($color-value);
47 | }
48 | .#{settings.$css-class-prefix}#{$class-family-name} {
49 | background-color: $value;
50 | @include foreground-color($color-value);
51 | }
52 | }
53 |
54 | // Shades
55 | @else if
56 | list.index(map.get(settings.$palette, "shades"), $shade) and
57 | map.get(settings.$palette, "enable-shades")
58 | {
59 | $value: $color-value;
60 | @if $enable-css-vars {
61 | $value: var(#{$css-var-family-name}-#{$shade});
62 | } @else {
63 | $value: utils.display-color($color-value);
64 | }
65 | .#{settings.$css-class-prefix}#{$class-family-name}-#{$shade} {
66 | background-color: $value;
67 | @include foreground-color($color-value);
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_colors.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "sass:list";
3 | @use "../../colors";
4 | @use "settings";
5 | @use "utils";
6 |
7 | @mixin colors {
8 | @if map.get(settings.$utilities, "colors") {
9 | $enable-css-vars: map.get(settings.$utilities, "css-vars");
10 | $color-property-name: map.get(settings.$properties, "color");
11 | $css-var-color-prefix: #{settings.$css-var-prefix}#{$color-property-name};
12 |
13 | // Loop through color families
14 | @each $family, $colors in colors.$colors {
15 | @if list.index(map.get(settings.$palette, "color-families"), $family) {
16 | $css-var-family-name: #{$css-var-color-prefix}-#{$family};
17 | $class-family-name: #{$color-property-name}-#{$family};
18 |
19 | // Loop through colors
20 | @each $shade, $color-value in $colors {
21 | // Main color
22 | @if $shade == "main" and map.get(settings.$palette, "enable-main-color") {
23 | @if $enable-css-vars {
24 | $color-value: var(#{$css-var-family-name});
25 | } @else {
26 | $color-value: utils.display-color($color-value);
27 | }
28 | .#{settings.$css-class-prefix}#{$class-family-name} {
29 | color: $color-value;
30 | }
31 | }
32 |
33 | // Shades
34 | @else if
35 | list.index(map.get(settings.$palette, "shades"), $shade) and
36 | map.get(settings.$palette, "enable-shades")
37 | {
38 | @if $enable-css-vars {
39 | $color-value: var(#{$css-var-family-name}-#{$shade});
40 | } @else {
41 | $color-value: utils.display-color($color-value);
42 | }
43 | .#{settings.$css-class-prefix}#{$class-family-name}-#{$shade} {
44 | color: $color-value;
45 | }
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_css-vars.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "sass:list";
3 | @use "../../colors";
4 | @use "settings";
5 | @use "utils";
6 |
7 | @mixin css-vars {
8 | $enable-css-vars: map.get(settings.$utilities, "css-vars");
9 | $color-property-name: map.get(settings.$properties, "color");
10 | $css-var-color-prefix: #{settings.$css-var-prefix}#{$color-property-name};
11 |
12 | @if $enable-css-vars {
13 | :root,
14 | :host {
15 | // Loop through color families
16 | @each $family, $colors in colors.$colors {
17 | @if list.index(map.get(settings.$palette, "color-families"), $family) {
18 | $css-var-family-name: #{$css-var-color-prefix}-#{$family};
19 |
20 | // Loop through colors
21 | @each $shade, $color-value in $colors {
22 | // Main color
23 | @if $shade == "main" and map.get(settings.$palette, "enable-main-color") {
24 | #{$css-var-family-name}: #{utils.display-color($color-value)};
25 | }
26 |
27 | // Shades
28 | @else if
29 | list.index(map.get(settings.$palette, "shades"), $shade) and
30 | map.get(settings.$palette, "enable-shades")
31 | {
32 | #{$css-var-family-name}-#{$shade}: #{utils.display-color($color-value)};
33 | }
34 | }
35 | }
36 | }
37 |
38 | // Black & white
39 | @if map.get(settings.$palette, "enable-black-and-white") {
40 | #{$css-var-color-prefix}-black: #{utils.display-color(colors.$black)};
41 | #{$css-var-color-prefix}-white: #{utils.display-color(colors.$white)};
42 | }
43 |
44 | // Text color variables
45 | @if map.get(settings.$utilities, "color-for-background-colors") {
46 | #{$css-var-color-prefix}-light: #{utils.display-color(
47 | map.get(settings.$palette, "light-color")
48 | )};
49 | #{$css-var-color-prefix}-dark: #{utils.display-color(
50 | map.get(settings.$palette, "dark-color")
51 | )};
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_index.scss:
--------------------------------------------------------------------------------
1 | @forward "settings";
2 | @use "css-vars" as *;
3 | @use "colors" as *;
4 | @use "background-colors" as *;
5 |
6 | @include css-vars;
7 | @include colors;
8 | @include background-colors;
9 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_settings.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../../settings" as pico-settings;
3 |
4 | // Prefix for CSS variables
5 | $css-var-prefix: "--pico-" !default; // Must start with "--"
6 | $css-class-prefix: "pico-" !default;
7 |
8 | // Palette
9 | $palette: () !default;
10 | $palette: map.merge(
11 | (
12 | // Color families
13 | "color-families": (
14 | red,
15 | pink,
16 | fuchsia,
17 | purple,
18 | violet,
19 | indigo,
20 | blue,
21 | azure,
22 | cyan,
23 | jade,
24 | green,
25 | lime,
26 | yellow,
27 | amber,
28 | pumpkin,
29 | orange,
30 | sand,
31 | grey,
32 | zinc,
33 | slate
34 | ),
35 | // Shades
36 | "shades": (
37 | 50,
38 | 100,
39 | 150,
40 | 200,
41 | 250,
42 | 300,
43 | 350,
44 | 400,
45 | 450,
46 | 500,
47 | 550,
48 | 600,
49 | 650,
50 | 700,
51 | 750,
52 | 800,
53 | 850,
54 | 900,
55 | 950
56 | ),
57 | // Export main color for each family
58 | "enable-main-color": true,
59 |
60 | // Export shades for each family
61 | "enable-shades": true,
62 |
63 | // Export black and white
64 | "enable-black-and-white": false,
65 |
66 | // Light color used for dark backgrounds
67 | "light-color": #fff,
68 |
69 | // Dark color used for light backgrounds
70 | "dark-color": #000,
71 |
72 | // Export as HEX, RGB or HSL values
73 | "export-as": "hex" // hex | rgb | hsl
74 | ),
75 | $palette
76 | );
77 |
78 | // Properties names used for CSS variables and classes
79 | // Useful if you want to shorten the names
80 | $properties: () !default;
81 | $properties: map.merge(
82 | (
83 | "color": "color",
84 | "background-color": "background",
85 | ),
86 | $properties
87 | );
88 |
89 | // Utilities to export
90 | $utilities: () !default;
91 | $utilities: map.merge(
92 | (
93 | // CSS Vars
94 | "css-vars": true,
95 |
96 | // Colors utility classes
97 | "colors": true,
98 |
99 | // Background color utility classes
100 | "background-colors": true,
101 |
102 | // Color value for background color utility classes
103 | "color-for-background-colors": true
104 | ),
105 | $utilities
106 | );
107 |
--------------------------------------------------------------------------------
/scss/colors/utilities/_utils.scss:
--------------------------------------------------------------------------------
1 | @use "sass:color";
2 | @use "sass:math";
3 | @use "sass:map";
4 | @use "sass:string";
5 | @use "settings";
6 |
7 | // Determines if the foreground needs to be light or dark
8 | // depending on the background color passed.
9 | // W3C reference: http://www.w3.org/TR/AERT#color-contrast
10 | // Inspiration: https://codepen.io/davidhalford/pen/ALrbEP
11 | @function foreground-brightness($background-color) {
12 | $background-color-brightness: brightness($background-color);
13 | $light-color-brightness: brightness(#ffffff);
14 |
15 | @if math.abs($background-color-brightness) < $light-color-brightness * 0.5 {
16 | @return "light";
17 | } @else {
18 | @return "dark";
19 | }
20 | }
21 |
22 | // Calculates the color brightness
23 | // Color brightness is determined by the following formula:
24 | // ((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
25 | @function brightness($color) {
26 | $color-brightness: round(
27 | math.div(
28 | (color.channel($color, "red", $space: rgb) * 299) +
29 | (color.channel($color, "green", $space: rgb) * 587) +
30 | (color.channel($color, "blue", $space: rgb) * 114),
31 | 1000
32 | )
33 | );
34 |
35 | @return $color-brightness;
36 | }
37 |
38 | // Returns the color as RGB, HSL or HEX
39 | @function display-color($color) {
40 | @if map.get(settings.$palette, "export-as") == "rgb" {
41 | @return display-rgb($color);
42 | }
43 | @if map.get(settings.$palette, "export-as") == "hsl" {
44 | @return display-hsl($color);
45 | }
46 | @return $color;
47 | }
48 |
49 | // Display color as HSL
50 | @function display-hsl($color) {
51 | @return unquote(
52 | "hsl(#{math.round(hue($color))}, #{math.round(saturation($color))}, #{math.round(lightness($color))})"
53 | );
54 | }
55 |
56 | // Display color as RGB
57 | @function display-rgb($color) {
58 | @return string.unquote(
59 | "rgb(" + color.channel($color, "red", $space: rgb) + ", " +
60 | color.channel($color, "green", $space: rgb) + ", " +
61 | color.channel($color, "blue", $space: rgb) + ")"
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/scss/components/_accordion.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/accordion") {
5 | /**
6 | * Accordion ()
7 | */
8 | #{$parent-selector} details {
9 | display: block;
10 | margin-block-end: var(#{$css-var-prefix}spacing);
11 |
12 | @if $enable-classes {
13 | &.hide-arrow > summary::after {
14 | display: none;
15 | content: none;
16 | }
17 | }
18 |
19 | &:not(.hide-arrow) > summary {
20 | padding-inline-end: calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * 3.25);
21 | }
22 |
23 | summary {
24 | position: relative;
25 | //padding-inline-end: var(#{$css-var-prefix}block-spacing-horizontal);
26 | line-height: 1rem;
27 | list-style-type: none;
28 | cursor: pointer;
29 | //@if $enable-transitions {
30 | //transition: color var(#{$css-var-prefix}transition);
31 | //}
32 |
33 | &:not([role]) {
34 | color: var(#{$css-var-prefix}accordion-close-summary-color);
35 | }
36 |
37 | // Reset marker
38 | &::-webkit-details-marker {
39 | display: none;
40 | }
41 |
42 | &::marker {
43 | display: none;
44 | }
45 |
46 | &::-moz-list-bullet {
47 | list-style-type: none;
48 | }
49 |
50 | // Marker
51 | &::after {
52 | display: block;
53 | position: absolute;
54 | top: calc(var(#{$css-var-prefix}block-spacing-vertical) * 0.5);
55 | right: calc(var(#{$css-var-prefix}block-spacing-horizontal) * 0.5);
56 | width: 1rem;
57 | height: 1rem;
58 | margin-inline-start: calc(var(#{$css-var-prefix}spacing, 1rem) * 0.5);
59 | transform: rotate(-90deg);
60 | background-image: var(#{$css-var-prefix}icon-chevron);
61 | background-position: right center;
62 | background-size: 1rem auto;
63 | background-repeat: no-repeat;
64 | content: "";
65 |
66 | @if $enable-transitions {
67 | transition: transform var(#{$css-var-prefix}transition);
68 | }
69 | }
70 |
71 | &:focus {
72 | outline: none;
73 |
74 | &:not([role]) {
75 | color: var(#{$css-var-prefix}accordion-active-summary-color);
76 | }
77 | }
78 |
79 | &:focus-visible {
80 | &:not([role]) {
81 | outline: var(#{$css-var-prefix}outline-width) solid var(#{$css-var-prefix}primary-focus);
82 | outline-offset: calc(var(#{$css-var-prefix}spacing, 1rem) * 0.5);
83 | color: var(#{$css-var-prefix}primary);
84 | }
85 | }
86 |
87 | // Type button
88 | &[role="button"] {
89 | width: 100%;
90 | text-align: left;
91 |
92 | // Marker
93 | &::after {
94 | height: calc(1rem * var(#{$css-var-prefix}line-height, 1.5));
95 | }
96 | }
97 | }
98 |
99 | // Open
100 | &[open] {
101 | > summary {
102 | margin-block-end: var(#{$css-var-prefix}spacing);
103 |
104 | &:not([role]):not(:focus) {
105 | color: var(#{$css-var-prefix}accordion-open-summary-color);
106 | }
107 |
108 | &::after {
109 | transform: rotate(0);
110 | }
111 | }
112 | }
113 | }
114 |
115 | [dir="rtl"] {
116 | #{$parent-selector} details {
117 | summary {
118 | text-align: right;
119 |
120 | &::after {
121 | float: left;
122 | transform: rotate(90deg);
123 | background-position: left center;
124 | }
125 | }
126 | &[open] {
127 | > summary {
128 | &::after {
129 | transform: rotate(0);
130 | }
131 | }
132 | }
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/scss/components/_card.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/card") or map.get($modules, "components/modal") {
5 | /**
6 | * Card (, role="article")
7 | */
8 |
9 | #{$parent-selector} article:not(:has(> form:first-child)),
10 | #{$parent-selector} [role="article"]:not(:has(> form:first-child)),
11 | #{$parent-selector} article > form,
12 | #{$parent-selector} [role="article"] > form {
13 | margin-bottom: var(#{$css-var-prefix}block-spacing-vertical);
14 | padding: var(#{$css-var-prefix}block-spacing-vertical)
15 | var(#{$css-var-prefix}block-spacing-horizontal);
16 | border-radius: var(#{$css-var-prefix}border-radius);
17 | background: var(#{$css-var-prefix}card-background-color);
18 | box-shadow: var(#{$css-var-prefix}card-box-shadow);
19 |
20 | > header,
21 | > footer {
22 | margin-right: calc(var(#{$css-var-prefix}block-spacing-horizontal) * -1);
23 | margin-left: calc(var(#{$css-var-prefix}block-spacing-horizontal) * -1);
24 | padding: calc(var(#{$css-var-prefix}block-spacing-vertical) * 0.66)
25 | var(#{$css-var-prefix}block-spacing-horizontal);
26 | background-color: var(#{$css-var-prefix}card-sectioning-background-color);
27 | }
28 |
29 | > header {
30 | margin-top: calc(var(#{$css-var-prefix}block-spacing-vertical) * -1);
31 | margin-bottom: var(#{$css-var-prefix}block-spacing-vertical);
32 | border-bottom: var(#{$css-var-prefix}border-width)
33 | solid
34 | var(#{$css-var-prefix}card-border-color);
35 | border-top-right-radius: var(#{$css-var-prefix}border-radius);
36 | border-top-left-radius: var(#{$css-var-prefix}border-radius);
37 |
38 | > h1,
39 | > h2,
40 | > h3,
41 | > h4,
42 | > h5,
43 | > h6 {
44 | margin-bottom: 0;
45 | }
46 |
47 | > hgroup {
48 | > h1,
49 | > h2,
50 | > h3,
51 | > h4,
52 | > h5,
53 | > h6 {
54 | margin-bottom: 0;
55 | }
56 | }
57 | }
58 |
59 | > footer {
60 | margin-top: var(#{$css-var-prefix}block-spacing-vertical);
61 | margin-bottom: calc(var(#{$css-var-prefix}block-spacing-vertical) * -1);
62 | border-top: var(#{$css-var-prefix}border-width)
63 | solid
64 | var(#{$css-var-prefix}card-border-color);
65 | border-bottom-right-radius: var(#{$css-var-prefix}border-radius);
66 | border-bottom-left-radius: var(#{$css-var-prefix}border-radius);
67 |
68 | // https://github.com/picocss/pico/issues/557#issuecomment-2393213110
69 | [type="submit"],
70 | [type="reset"],
71 | [type="button"],
72 | [role="group"],
73 | p,
74 | nav,
75 | ul,
76 | ol {
77 | margin-bottom: 0px;
78 |
79 | &:last-child {
80 | margin-bottom: 0px;
81 | }
82 |
83 | /* Also remove if next input after button is a hidden input */
84 | &:has(+ [type="hidden"]) {
85 | margin-bottom: 0px;
86 | }
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/scss/components/_dropdown.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/dropdown") and $enable-classes {
5 | /**
6 | * Dropdown (details.dropdown)
7 | */
8 |
9 | // Container
10 | // ––––––––––––––––––––
11 | #{$parent-selector} details.dropdown {
12 | position: relative;
13 | border-bottom: none;
14 |
15 | @if $enable-transitions {
16 | &[open] {
17 | > summary,
18 | > button,
19 | > a {
20 | &::after {
21 | transform: rotate(0deg);
22 | }
23 | }
24 | }
25 | }
26 |
27 | // Marker
28 | // ––––––––––––––––––––
29 | > summary,
30 | > button,
31 | > a {
32 | &::after {
33 | display: block;
34 | width: 1rem;
35 | height: calc(1rem * var(#{$css-var-prefix}line-height, 1.5));
36 | margin-inline-start: 0.25rem;
37 | float: right;
38 | // TODO: find out why we need this magic number (0.2 rem)
39 | // for the marker to be aligned with the regular select
40 | //transform: rotate(0deg) translateX(0.2rem);
41 | transform: rotate(-90deg);
42 | background-image: var(#{$css-var-prefix}icon-chevron);
43 | background-position: right center;
44 | background-size: 1rem auto;
45 | background-repeat: no-repeat;
46 | content: "";
47 | }
48 | }
49 |
50 | // Button as a select
51 | // inside container type accordion
52 | // ––––––––––––––––––––
53 | > summary {
54 | &:not([role]) {
55 | height: calc(
56 | 1rem *
57 | var(#{$css-var-prefix}line-height) +
58 | var(#{$css-var-prefix}form-element-spacing-vertical) *
59 | 2 +
60 | var(#{$css-var-prefix}border-width) *
61 | 2
62 | );
63 | padding: var(#{$css-var-prefix}form-element-spacing-vertical)
64 | var(#{$css-var-prefix}form-element-spacing-horizontal);
65 | border: var(#{$css-var-prefix}border-width)
66 | solid
67 | var(#{$css-var-prefix}form-element-border-color);
68 | border-radius: var(#{$css-var-prefix}border-radius);
69 | background-color: var(#{$css-var-prefix}form-element-background-color);
70 | color: var(#{$css-var-prefix}form-element-placeholder-color);
71 | line-height: inherit;
72 | text-align: inherit;
73 | cursor: pointer;
74 | user-select: none;
75 |
76 | @if $enable-transitions {
77 | transition:
78 | background-color var(#{$css-var-prefix}transition),
79 | border-color var(#{$css-var-prefix}transition),
80 | color var(#{$css-var-prefix}transition),
81 | box-shadow var(#{$css-var-prefix}transition);
82 | }
83 |
84 | &:active,
85 | &:focus {
86 | border-color: var(#{$css-var-prefix}form-element-active-border-color);
87 | background-color: var(#{$css-var-prefix}form-element-active-background-color);
88 | }
89 |
90 | &:focus {
91 | box-shadow: 0
92 | 0
93 | 0
94 | var(#{$css-var-prefix}outline-width)
95 | var(#{$css-var-prefix}form-element-focus-color);
96 | }
97 |
98 | // Reset focus visible from accordion component
99 | &:focus-visible {
100 | outline: none;
101 | }
102 |
103 | // Aria-invalid
104 | &[aria-invalid="false"] {
105 | #{$css-var-prefix}form-element-border-color: var(
106 | #{$css-var-prefix}form-element-valid-border-color
107 | );
108 | #{$css-var-prefix}form-element-active-border-color: var(
109 | #{$css-var-prefix}form-element-valid-focus-color
110 | );
111 | #{$css-var-prefix}form-element-focus-color: var(
112 | #{$css-var-prefix}form-element-valid-focus-color
113 | );
114 | }
115 |
116 | &[aria-invalid="true"] {
117 | #{$css-var-prefix}form-element-border-color: var(
118 | #{$css-var-prefix}form-element-invalid-border-color
119 | );
120 | #{$css-var-prefix}form-element-active-border-color: var(
121 | #{$css-var-prefix}form-element-invalid-focus-color
122 | );
123 | #{$css-var-prefix}form-element-focus-color: var(
124 | #{$css-var-prefix}form-element-invalid-focus-color
125 | );
126 | }
127 | }
128 |
129 | // Submenu
130 | // ––––––––––––––––––––
131 | + ul {
132 | display: flex;
133 | z-index: 99;
134 | position: absolute;
135 | left: 0;
136 | flex-direction: column;
137 | width: 100%;
138 | min-width: 200px;
139 | min-width: fit-content;
140 | margin: 0;
141 | margin-block-start: var(#{$css-var-prefix}outline-width);
142 | padding: 0;
143 | border: var(#{$css-var-prefix}border-width)
144 | solid
145 | var(#{$css-var-prefix}dropdown-border-color);
146 | border-radius: var(#{$css-var-prefix}border-radius);
147 | background-color: var(#{$css-var-prefix}dropdown-background-color);
148 | box-shadow: var(#{$css-var-prefix}dropdown-box-shadow);
149 | color: var(#{$css-var-prefix}dropdown-color);
150 | white-space: nowrap;
151 | opacity: 0;
152 |
153 | @if $enable-transitions {
154 | transition:
155 | opacity var(#{$css-var-prefix}transition),
156 | transform 0s ease-in-out 1s;
157 | }
158 |
159 | &[dir="rtl"] {
160 | right: 0;
161 | left: auto;
162 | }
163 |
164 | li {
165 | width: 100%;
166 | margin-block-end: 0;
167 | padding: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5)
168 | var(#{$css-var-prefix}form-element-spacing-horizontal);
169 | list-style: none;
170 |
171 | &:first-of-type {
172 | margin-block-start: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5);
173 | }
174 |
175 | &:last-of-type {
176 | margin-block-end: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5);
177 | }
178 |
179 | details {
180 | width: 100%;
181 | margin-block-end: 0;
182 | > summary {
183 | line-height: var(#{$css-var-prefix}line-height);
184 | }
185 | }
186 |
187 | a {
188 | display: block;
189 | margin: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * -0.5)
190 | calc(var(#{$css-var-prefix}form-element-spacing-horizontal) * -1);
191 | padding: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5)
192 | var(#{$css-var-prefix}form-element-spacing-horizontal);
193 | overflow: hidden;
194 | border-radius: 0;
195 | color: var(#{$css-var-prefix}dropdown-color);
196 | text-decoration: none;
197 | text-overflow: ellipsis;
198 |
199 | &:hover,
200 | &:focus,
201 | &:active,
202 | &:focus-visible,
203 | &[aria-current]:not([aria-current="false"]) {
204 | background-color: var(#{$css-var-prefix}dropdown-hover-background-color);
205 | }
206 | }
207 |
208 | label {
209 | width: 100%;
210 | }
211 |
212 | // Not working in Firefox, which doesn't support the `:has()` pseudo-class
213 | &:has(label):hover {
214 | background-color: var(#{$css-var-prefix}dropdown-hover-background-color);
215 | }
216 | }
217 | }
218 | }
219 |
220 | // Menu opened
221 | // ––––––––––––––––––––
222 | // 1. Inside container type accordion
223 | &[open] > summary {
224 | // Button opened
225 | // inside container type accordion
226 | // ––––––––––––––––––––
227 | margin-block-end: 0;
228 |
229 | // Close for dropdown
230 | // inside container type accordion
231 | // ––––––––––––––––––––
232 | &::before {
233 | display: block;
234 | z-index: 1;
235 | position: fixed;
236 | width: 100vw;
237 | height: 100vh;
238 | inset: 0;
239 | background: none;
240 | content: "";
241 | cursor: default;
242 | }
243 |
244 | + ul {
245 | transform: scaleY(1);
246 | opacity: 1;
247 |
248 | @if $enable-transitions {
249 | transition:
250 | opacity var(#{$css-var-prefix}transition),
251 | transform 0s ease-in-out 0s;
252 | }
253 | }
254 | }
255 | }
256 |
257 | // Container type accordion
258 | // inside a nav
259 | // ––––––––––––––––––––
260 | // Dropdown inside a nav
261 | // ––––––––––––––––––––
262 | #{$parent-selector} nav details.dropdown {
263 | display: inline;
264 | margin: calc(var(#{$css-var-prefix}nav-element-spacing-vertical) * -1) 0;
265 | // Override height
266 | margin-block-end: 0;
267 |
268 | &.hide-arrow {
269 | > summary::after {
270 | display: none;
271 | content: none;
272 | }
273 | }
274 |
275 | > summary {
276 | &:not([role]) {
277 | // Override height
278 | height: calc(
279 | (1rem * var(#{$css-var-prefix}line-height)) +
280 | (var(#{$css-var-prefix}nav-link-spacing-vertical) * 2)
281 | );
282 | padding: calc(
283 | var(#{$css-var-prefix}nav-link-spacing-vertical) -
284 | (var(#{$css-var-prefix}border-width) * 2)
285 | )
286 | var(#{$css-var-prefix}nav-link-spacing-horizontal);
287 |
288 | &:focus-visible {
289 | box-shadow: 0
290 | 0
291 | 0
292 | var(#{$css-var-prefix}outline-width)
293 | var(#{$css-var-prefix}primary-focus);
294 | }
295 | }
296 | }
297 |
298 | &:not(.hide-arrow) > summary {
299 | padding-inline-end: calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * 3.25);
300 | }
301 | }
302 |
303 | // Label
304 | // ––––––––––––––––––––
305 | #{$parent-selector} label > details.dropdown {
306 | margin-block-start: calc(var(#{$css-var-prefix}spacing) * 0.25);
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/scss/components/_group.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/group") {
5 | /**
6 | * Group ([role="group"], [role="search"])
7 | */
8 |
9 | /* V3 Remove, modifing the placement is disabled
10 | * in chrome.*/
11 | #{$parent-selector} fieldset[role="group"] > legend {
12 | float: left;
13 | }
14 |
15 | #{$parent-selector} [role="search"],
16 | #{$parent-selector} [role="group"] {
17 | display: inline-flex;
18 | position: relative;
19 | width: 100%;
20 | margin-bottom: var(#{$css-var-prefix}spacing);
21 | border-radius: var(#{$css-var-prefix}border-radius);
22 | box-shadow: var(#{$css-var-prefix}group-box-shadow, 0 0 0 rgba(0, 0, 0, 0));
23 | vertical-align: middle;
24 | transition: box-shadow var(#{$css-var-prefix}transition);
25 |
26 | > label,
27 | > legend {
28 | border-color: var(#{$css-var-prefix}secondary-background);
29 | background-color: var(#{$css-var-prefix}secondary-background);
30 | color: var(#{$css-var-prefix}secondary-inverse);
31 | }
32 |
33 | > *,
34 | input:not([type="checkbox"], [type="radio"]),
35 | select,
36 | > label,
37 | > legend,
38 | > details {
39 | position: relative;
40 | flex: 1 1 auto;
41 | margin-bottom: 0;
42 |
43 | &:not(:first-child) {
44 | margin-left: 0;
45 | border-top-left-radius: 0;
46 | border-bottom-left-radius: 0;
47 | }
48 |
49 | &:not(:last-child) {
50 | border-top-right-radius: 0;
51 | border-bottom-right-radius: 0;
52 | }
53 |
54 | &:focus {
55 | z-index: 2;
56 | }
57 | }
58 |
59 | > details {
60 | &:not(:first-child) > summary {
61 | margin-left: 0;
62 | border-top-left-radius: 0;
63 | border-bottom-left-radius: 0;
64 | }
65 |
66 | &:not(:last-child) > summary {
67 | border-top-right-radius: 0;
68 | border-bottom-right-radius: 0;
69 | }
70 | > summary,
71 | > summary:not([role]) {
72 | height: 100% !important;
73 | }
74 | }
75 |
76 | button,
77 | [type="submit"],
78 | [type="reset"],
79 | [type="button"],
80 | [role="button"],
81 | input:not([type="checkbox"], [type="radio"]),
82 | select,
83 | > label,
84 | > legend,
85 | > details > summary {
86 | &:not(:first-child) {
87 | margin-left: calc(var(#{$css-var-prefix}border-width) * -1);
88 | }
89 | }
90 |
91 | button,
92 | [type="submit"],
93 | [type="reset"],
94 | [type="button"],
95 | [role="button"],
96 | > label,
97 | > legend,
98 | > details > summary {
99 | width: auto;
100 | }
101 |
102 | @supports selector(:has(*)) {
103 | // Group box shadow when a button is focused
104 | &:has(button:focus, [type="submit"]:focus, [type="button"]:focus, [role="button"]:focus) {
105 | #{$css-var-prefix}group-box-shadow: var(
106 | #{$css-var-prefix}group-box-shadow-focus-with-button
107 | );
108 |
109 | input:not([type="checkbox"], [type="radio"]),
110 | select,
111 | > label,
112 | > legend,
113 | > details > summary {
114 | border-color: transparent;
115 | }
116 | }
117 |
118 | // Group box shadow when an input is focused
119 | &:has(input:not([type="submit"], [type="button"]):focus, select:focus) {
120 | #{$css-var-prefix}group-box-shadow: var(
121 | #{$css-var-prefix}group-box-shadow-focus-with-input
122 | );
123 |
124 | // Adapt box shadow for buttons
125 | button,
126 | [type="submit"],
127 | [type="button"],
128 | [role="button"],
129 | label,
130 | > legend,
131 | > details > summary {
132 | #{$css-var-prefix}button-box-shadow: 0 0 0 var(#{$css-var-prefix}border-width)
133 | var(#{$css-var-prefix}primary-border);
134 | #{$css-var-prefix}button-hover-box-shadow: 0 0 0 var(#{$css-var-prefix}border-width)
135 | var(#{$css-var-prefix}primary-hover-border);
136 | }
137 | }
138 |
139 | // Remove button box shadow if we have a group box shadow
140 | button,
141 | [type="submit"],
142 | [type="reset"],
143 | [type="button"],
144 | [role="button"],
145 | label,
146 | > legend,
147 | > details > summary {
148 | &:focus {
149 | box-shadow: none;
150 | }
151 | }
152 | }
153 | }
154 |
155 | #{$parent-selector} [role="search"] {
156 | > * {
157 | &:first-child {
158 | border-top-left-radius: 5rem;
159 | border-bottom-left-radius: 5rem;
160 | }
161 |
162 | &:last-child {
163 | border-top-right-radius: 5rem;
164 | border-bottom-right-radius: 5rem;
165 | }
166 | }
167 | }
168 |
169 | [dir="rtl"] {
170 | #{$parent-selector} [role="search"],
171 | #{$parent-selector} [role="group"] {
172 | > *,
173 | input:not([type="checkbox"], [type="radio"]),
174 | select,
175 | label,
176 | > legend,
177 | > details > summary {
178 | &:not(:first-child) {
179 | margin-right: 0;
180 | margin-left: auto;
181 | border-top-right-radius: 0;
182 | border-top-left-radius: var(#{$css-var-prefix}border-radius);
183 | border-bottom-right-radius: 0;
184 | border-bottom-left-radius: var(#{$css-var-prefix}border-radius);
185 | }
186 |
187 | &:not(:last-child) {
188 | border-top-right-radius: var(#{$css-var-prefix}border-radius);
189 | border-top-left-radius: 0;
190 | border-bottom-right-radius: var(#{$css-var-prefix}border-radius);
191 | border-bottom-left-radius: 0;
192 | }
193 | }
194 |
195 | button,
196 | [type="submit"],
197 | [type="reset"],
198 | [type="button"],
199 | [role="button"],
200 | input:not([type="checkbox"], [type="radio"]),
201 | select,
202 | label,
203 | > legend,
204 | > details > summary {
205 | &:not(:first-child) {
206 | margin-right: calc(var(#{$css-var-prefix}border-width) * -1);
207 | margin-left: auto;
208 | }
209 | }
210 | }
211 | }
212 | }
213 |
214 | @if map.get($modules, "forms/floating") {
215 | #{$parent-selector} [role="group"] > section[role="form"] {
216 | flex: 1;
217 |
218 | > *,
219 | > input:not([type="checkbox"], [type="radio"]),
220 | > select,
221 | > label,
222 | > legend,
223 | > details {
224 | // need to check this for details > summary
225 | &:focus {
226 | z-index: 4;
227 | }
228 | }
229 |
230 | > input:not([type="checkbox"], [type="radio"]),
231 | > select {
232 | &:focus {
233 | z-index: 10;
234 | }
235 | + label {
236 | z-index: 10;
237 | // label was positioned above placeholder..
238 | margin-top: 5px;
239 | }
240 | }
241 |
242 | &:not(:first-child) {
243 | > *,
244 | > input:not([type="checkbox"], [type="radio"]),
245 | > select,
246 | > label,
247 | > legend,
248 | > details > summary {
249 | margin-left: 0;
250 | border-top-left-radius: 0;
251 | border-bottom-left-radius: 0;
252 | }
253 | }
254 |
255 | &:not(:last-child) {
256 | > *,
257 | > input:not([type="checkbox"], [type="radio"]),
258 | > select,
259 | > label,
260 | > legend,
261 | > details > summary {
262 | border-top-right-radius: 0;
263 | border-bottom-right-radius: 0;
264 | }
265 | }
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/scss/components/_loading.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/loading") {
5 | /**
6 | * Loading ([aria-busy=true])
7 | */
8 |
9 | // Everything except form elements
10 | #{$parent-selector} [aria-busy="true"]:not(input, select, textarea, html, form) {
11 | white-space: nowrap;
12 |
13 | &::before {
14 | display: inline-block;
15 | width: 1em;
16 | height: 1em;
17 | background-image: var(#{$css-var-prefix}icon-loading);
18 | background-size: 1em auto;
19 | background-repeat: no-repeat;
20 | content: "";
21 | vertical-align: -0.125em; // Visual alignment
22 | }
23 |
24 | &:not(:empty) {
25 | &::before {
26 | margin-inline-end: calc(var(#{$css-var-prefix}spacing) * 0.5);
27 | }
28 | }
29 |
30 | &:empty {
31 | text-align: center;
32 | }
33 | }
34 |
35 | // Buttons and links
36 | #{$parent-selector} button,
37 | #{$parent-selector} [type="submit"],
38 | #{$parent-selector} [type="button"],
39 | #{$parent-selector} [type="reset"],
40 | #{$parent-selector} [role="button"],
41 | #{$parent-selector} a {
42 | &[aria-busy="true"] {
43 | pointer-events: none;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/scss/components/_modal.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/modal") {
5 | /**
6 | * Modal ()
7 | */
8 |
9 | :root,
10 | :host {
11 | #{$css-var-prefix}scrollbar-width: 0px;
12 | }
13 |
14 | #{$parent-selector} dialog:not(.modal-fs, .modal-xlg, .modal-lg, .modal-md, .modal-sm) {
15 | > article:not(:has(> form:first-child)),
16 | > article > form:first-child {
17 | width: 100%;
18 |
19 | @if map.get($breakpoints, "sm") {
20 | @media (min-width: map.get(map.get($breakpoints, "sm"), "breakpoint")) {
21 | max-width: map.get(map.get($breakpoints, "sm"), "viewport");
22 | }
23 | }
24 |
25 | @if map.get($breakpoints, "md") {
26 | @media (min-width: map.get(map.get($breakpoints, "md"), "breakpoint")) {
27 | max-width: map.get(map.get($breakpoints, "md"), "viewport");
28 | }
29 | }
30 | }
31 | }
32 |
33 | #{$parent-selector} dialog {
34 | display: grid;
35 | z-index: 999;
36 | position: fixed;
37 | top: 0;
38 | right: 0;
39 | bottom: 0;
40 | left: 0;
41 | align-items: center;
42 | justify-content: center;
43 | width: inherit;
44 | min-width: 100%;
45 | height: inherit;
46 | min-height: 100%;
47 | padding: var(#{$css-var-prefix}spacing);
48 | border: 0;
49 | backdrop-filter: var(#{$css-var-prefix}modal-overlay-backdrop-filter);
50 | background-color: var(#{$css-var-prefix}modal-overlay-background-color);
51 | color: var(#{$css-var-prefix}color);
52 |
53 | @if $enable-transitions {
54 | transform: scale(1);
55 | transition: transform var(#{$css-var-prefix}transition);
56 | }
57 |
58 | // Content
59 | > article:not(:has(> form:first-child)),
60 | > article > form:first-child {
61 | $close-selector: if(
62 | $enable-classes,
63 | ".close, :is(a, button)[rel=prev]",
64 | ":is(a, button)[rel=prev]"
65 | );
66 |
67 | > header {
68 | > * {
69 | margin-bottom: 0;
70 | }
71 |
72 | #{$close-selector} {
73 | margin: 0;
74 | margin-left: var(#{$css-var-prefix}spacing);
75 | padding: 0;
76 | float: right;
77 | }
78 | }
79 |
80 | > footer {
81 | text-align: right;
82 |
83 | button,
84 | [role="button"] {
85 | margin-bottom: 0;
86 |
87 | &:not(:first-of-type) {
88 | margin-left: calc(var(#{$css-var-prefix}spacing) * 0.5);
89 | }
90 | }
91 | }
92 |
93 | // Close icon
94 | #{$close-selector} {
95 | display: block;
96 | width: 1rem;
97 | height: 1rem;
98 | margin-top: calc(var(#{$css-var-prefix}spacing) * -1);
99 | margin-bottom: var(#{$css-var-prefix}spacing);
100 | margin-left: auto;
101 | border: none;
102 | background-image: var(#{$css-var-prefix}icon-close);
103 | background-position: center;
104 | background-size: auto 1rem;
105 | background-repeat: no-repeat;
106 | background-color: transparent;
107 | opacity: 0.5;
108 |
109 | @if $enable-transitions {
110 | transition: opacity var(#{$css-var-prefix}transition);
111 | }
112 |
113 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
114 | opacity: 1;
115 | }
116 | }
117 | }
118 |
119 | // Closed state
120 | &:not([open]),
121 | &[open="false"] {
122 | visibility: hidden;
123 | opacity: 0;
124 |
125 | & article {
126 | transform: scale(0.7);
127 | }
128 | }
129 | }
130 |
131 | // Utilities
132 | @if $enable-classes {
133 | .modal-is-open {
134 | padding-right: var(#{$css-var-prefix}scrollbar-width, 0px);
135 | overflow: hidden;
136 | pointer-events: none;
137 | touch-action: none;
138 |
139 | dialog {
140 | pointer-events: auto;
141 | touch-action: auto;
142 | }
143 | }
144 | dialog {
145 | // Small modal
146 | &.modal-sm {
147 | > article {
148 | width: 90vw;
149 | max-width: 400px;
150 | }
151 | }
152 |
153 | // Medium modal (default)
154 | &.modal-md {
155 | > article {
156 | width: 90vw;
157 | max-width: 600px;
158 | }
159 | }
160 |
161 | // Large modal
162 | &.modal-lg {
163 | > article {
164 | width: 90vw;
165 | max-width: 800px;
166 | }
167 | }
168 |
169 | // Extra large modal
170 | &.modal-xlg {
171 | > article {
172 | width: 95vw;
173 | max-width: 1000px;
174 | }
175 | }
176 |
177 | // Fullscreen modal
178 | &.modal-fs {
179 | padding: 0;
180 | > article {
181 | display: flex;
182 | flex-direction: column;
183 | justify-content: space-between;
184 | width: 100vw;
185 | max-width: 100vw;
186 | height: 100vh;
187 | max-height: 100vh;
188 | //border-radius: 0;
189 | margin: 0;
190 | //padding: var(--spacing);
191 | overflow-y: auto;
192 | > footer {
193 | display: flex;
194 | justify-content: flex-end;
195 | margin-top: auto;
196 | }
197 | }
198 | }
199 |
200 | // Handle mobile responsiveness
201 | @media (max-width: 576px) {
202 | &:not(.modal-fs) {
203 | > article {
204 | width: 95vw;
205 | }
206 | }
207 | }
208 | }
209 | }
210 |
211 | // Prevent scrolling body when modal is open
212 | body:has(dialog[open]) {
213 | overflow: hidden;
214 | }
215 |
216 | // Animations
217 | @if $enable-classes and $enable-transitions {
218 | $animation-duration: 0.2s;
219 |
220 | :where(.modal-is-opening, .modal-is-closing) {
221 | dialog,
222 | dialog > article {
223 | animation-duration: $animation-duration;
224 | animation-timing-function: ease-in-out;
225 | animation-fill-mode: both;
226 | }
227 |
228 | dialog {
229 | animation-duration: ($animation-duration * 4);
230 | animation-name: modal-overlay;
231 |
232 | > article {
233 | animation-delay: $animation-duration;
234 | animation-name: modal;
235 | }
236 | }
237 | }
238 |
239 | .modal-is-closing {
240 | dialog,
241 | dialog > article {
242 | animation-delay: 0s;
243 | animation-direction: reverse;
244 | }
245 | }
246 |
247 | @keyframes modal-overlay {
248 | from {
249 | backdrop-filter: none;
250 | background-color: transparent;
251 | }
252 | }
253 |
254 | @keyframes modal {
255 | from {
256 | transform: translateY(-100%);
257 | opacity: 0;
258 | }
259 | }
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/scss/components/_nav-hamburger.scss:
--------------------------------------------------------------------------------
1 | @use "sass:string";
2 | @use "sass:map";
3 | @use "sass:math";
4 | @use "../settings" as *;
5 |
6 | @if map.get($modules, "components/nav") {
7 | /**
8 | * Nav hamburger menu
9 | * modified from https: //codepen.io/brentarias/pen/gOQybod
10 | */
11 | #{$parent-selector} nav[role="navigation"] {
12 | z-index: 1;
13 | //align-items: center; // the label for the burger was bouncing
14 | width: 100%;
15 | overflow: visible;
16 |
17 | &[data-position="start"] {
18 | /* remove the 'flex-direction' to move menu to the right */
19 | flex-direction: row-reverse;
20 | }
21 |
22 | > input[type="checkbox"] {
23 | display: none;
24 | user-select: none;
25 | }
26 |
27 | > label {
28 | display: none;
29 | cursor: pointer;
30 | user-select: none;
31 | }
32 | > ul,
33 | > ol {
34 | > li > [role="search"] {
35 | margin-block-end: 0;
36 | }
37 | }
38 | }
39 |
40 | @each $breakpoint, $values in $breakpoints {
41 | $databp: 'nav[role="navigation"]';
42 | @if $breakpoint != sm {
43 | $databp: #{$databp} + "[data-breakpoint='" + $breakpoint + "']";
44 | }
45 |
46 | $viewport: map.get($values, "viewport");
47 | @media (max-width: $viewport) {
48 | #{$parent-selector} #{$databp} {
49 | flex-wrap: wrap;
50 | overflow: hidden;
51 |
52 | &:has(input[type="checkbox"]:checked) {
53 | overflow: visible;
54 | }
55 |
56 | label {
57 | display: block;
58 | }
59 | > [role="list"] {
60 | display: none;
61 | flex-direction: column;
62 | align-items: flex-start;
63 | width: 90vw;
64 | max-height: 0;
65 | margin: 0 auto;
66 | background-color: var(#{$css-var-prefix}muted-border-color); //muted-border-color);
67 | box-shadow: var(#{$css-var-prefix}box-shadow);
68 | opacity: 0;
69 | @if $enable-transitions {
70 | transition:
71 | max-height var(#{$css-var-prefix}transition),
72 | opacity var(#{$css-var-prefix}transition);
73 | }
74 | li {
75 | width: calc(100% - calc(var(#{$css-var-prefix}nav-link-spacing-vertical) * 2));
76 | margin: calc(var(#{$css-var-prefix}nav-link-spacing-vertical) * 0.5)
77 | var(#{$css-var-prefix}nav-link-spacing-vertical);
78 | padding: 0;
79 | > details.dropdown {
80 | width: 100%;
81 | }
82 | > form[role="search"] {
83 | margin-block-end: 0;
84 | }
85 | }
86 | a {
87 | display: block;
88 | margin: 0;
89 | border-radius: 0;
90 | border-block-end: 1px solid transparent;
91 | @if $enable-transitions {
92 | transition:
93 | border-color var(#{$css-var-prefix}transition),
94 | color var(#{$css-var-prefix}transition);
95 | }
96 | }
97 | a:hover {
98 | border-bottom-color: var(#{$css-var-prefix}underline);
99 | text-decoration: none;
100 | //background-color: var(#{$css-var-prefix}primary-background) !important;
101 | //color: var(#{$css-var-prefix}primary-inverse);
102 | }
103 | }
104 | input[type="checkbox"]:checked ~ [role="list"] {
105 | display: block;
106 | max-height: 100vh;
107 | opacity: 1;
108 | }
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/scss/components/_nav.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/nav") {
5 | /**
6 | * Nav
7 | */
8 |
9 | // Reboot based on :
10 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
11 |
12 | // Prevent VoiceOver from ignoring list semantics in Safari (opinionated)
13 | // https://github.com/picocss/pico/issues/634#issuecomment-2541600508
14 | :where(nav li)::before {
15 | float: none; // fixes safari bug in dropdowns. Previously was: left
16 | content: " "; //\200B"; // makes voice overs realize its a list item
17 | //@use "sass:string"; // if we want to use the original \200B content value, we need to add this line to the top.
18 | //content: string.unquote('"\\200B"'); // created a big gap between list items
19 | }
20 |
21 | // Pico
22 | // ––––––––––––––––––––
23 |
24 | #{$parent-selector} nav,
25 | #{$parent-selector} nav ul {
26 | display: flex;
27 | }
28 |
29 | #{$parent-selector} nav {
30 | justify-content: space-between;
31 | overflow: visible;
32 |
33 | ol,
34 | ul {
35 | align-items: center;
36 | margin-bottom: 0;
37 | padding: 0;
38 | list-style: none;
39 |
40 | &:first-of-type {
41 | margin-left: calc(var(#{$css-var-prefix}nav-element-spacing-horizontal) * -1);
42 | }
43 | &:last-of-type {
44 | margin-right: calc(var(#{$css-var-prefix}nav-element-spacing-horizontal) * -1);
45 | }
46 | }
47 |
48 | li {
49 | display: inline-block;
50 | margin: 0;
51 | padding: var(#{$css-var-prefix}nav-element-spacing-vertical)
52 | var(#{$css-var-prefix}nav-element-spacing-horizontal);
53 |
54 | :where(a, [role="link"]) {
55 | display: inline-block;
56 | margin: calc(var(#{$css-var-prefix}nav-link-spacing-vertical) * -1)
57 | calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * -1);
58 | padding: var(#{$css-var-prefix}nav-link-spacing-vertical)
59 | var(#{$css-var-prefix}nav-link-spacing-horizontal);
60 | border-radius: var(#{$css-var-prefix}border-radius);
61 |
62 | &:not(:hover) {
63 | text-decoration: none;
64 | }
65 | }
66 |
67 | // Minimal support for buttons and forms elements
68 | button,
69 | [role="button"],
70 | [type="button"],
71 | input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]),
72 | select {
73 | height: auto;
74 | margin-right: inherit;
75 | margin-bottom: 0;
76 | margin-left: inherit;
77 | padding: calc(
78 | var(#{$css-var-prefix}nav-link-spacing-vertical) -
79 | (var(#{$css-var-prefix}border-width) * 2)
80 | )
81 | var(#{$css-var-prefix}nav-link-spacing-horizontal);
82 | }
83 | }
84 |
85 | // Breadcrumb
86 | &[aria-label="breadcrumb"] {
87 | align-items: center;
88 | justify-content: start;
89 |
90 | & ul li {
91 | &:not(:first-child) {
92 | margin-inline-start: var(#{$css-var-prefix}nav-link-spacing-horizontal);
93 | }
94 |
95 | a {
96 | margin: calc(var(#{$css-var-prefix}nav-link-spacing-vertical) * -1) 0;
97 | margin-inline-start: calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * -1);
98 | }
99 |
100 | &:not(:last-child) {
101 | &::after {
102 | display: inline-block;
103 | position: absolute;
104 | width: calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * 4);
105 | margin: 0 calc(var(#{$css-var-prefix}nav-link-spacing-horizontal) * -1);
106 | content: var(#{$css-var-prefix}nav-breadcrumb-divider);
107 | color: var(#{$css-var-prefix}muted-color);
108 | text-align: center;
109 | text-decoration: none;
110 | white-space: nowrap;
111 | }
112 | }
113 | }
114 |
115 | // Minimal support for aria-current
116 | & a[aria-current]:not([aria-current="false"]) {
117 | background-color: transparent;
118 | color: inherit;
119 | text-decoration: none;
120 | pointer-events: none;
121 | }
122 | }
123 | }
124 |
125 | // Vertical Nav
126 | #{$parent-selector} aside {
127 | nav,
128 | ol,
129 | ul,
130 | li {
131 | display: block;
132 | }
133 |
134 | li {
135 | padding: calc(var(#{$css-var-prefix}nav-element-spacing-vertical) * 0.5)
136 | var(#{$css-var-prefix}nav-element-spacing-horizontal);
137 |
138 | a {
139 | display: block;
140 | }
141 |
142 | // Minimal support for links as buttons
143 | [role="button"] {
144 | margin: inherit;
145 | }
146 | }
147 | }
148 |
149 | // Breadcrumb RTL
150 | [dir="rtl"] {
151 | #{$parent-selector} nav {
152 | &[aria-label="breadcrumb"] {
153 | & ul li {
154 | &:not(:last-child) {
155 | ::after {
156 | content: "\\";
157 | }
158 | }
159 | }
160 | }
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/scss/components/_notification.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/notification") {
5 | /**
6 | * Notification ()
7 | */
8 |
9 | #{$parent-selector} dialog[role="alert"] {
10 | position: fixed;
11 | top: unset;
12 | right: var(#{$css-var-prefix}spacing);
13 | bottom: var(#{$css-var-prefix}spacing);
14 | left: unset;
15 | width: auto;
16 | min-width: unset;
17 | height: auto;
18 | min-height: unset;
19 | padding: var(#{$css-var-prefix}form-element-spacing-vertical)
20 | var(#{$css-var-prefix}form-element-spacing-horizontal);
21 | border: var(#{$css-var-prefix}border-width) solid var(#{$css-var-prefix}border-color);
22 | border-radius: var(#{$css-var-prefix}border-radius);
23 | backdrop-filter: var(#{$css-var-prefix}modal-overlay-backdrop-filter);
24 | background-color: var(#{$css-var-prefix}primary-background);
25 | box-shadow: var(#{$css-var-prefix}box-shadow);
26 | color: var(#{$css-var-prefix}primary-inverse);
27 | font-weight: var(#{$css-var-prefix}font-weight);
28 | font-size: var(#{$css-var-prefix}font-size);
29 | line-height: var(#{$css-var-prefix}line-height);
30 | opacity: 0;
31 | transition: opacity var(#{$css-var-prefix}transition);
32 | user-select: none;
33 |
34 | //> p, > h1, > h2, > h3, > h4, > h5, > h6 {
35 | * {
36 | color: var(#{$css-var-prefix}primary-inverse);
37 | font-weight: var(#{$css-var-prefix}font-weight);
38 | font-size: var(#{$css-var-prefix}font-size);
39 | line-height: var(#{$css-var-prefix}line-height);
40 | }
41 | }
42 |
43 | #{$parent-selector} dialog[data-backdrop="false"][role="alert"]::backdrop {
44 | display: none;
45 | }
46 |
47 | #{$parent-selector} dialog[role="alert"][open] {
48 | opacity: 1;
49 | transition: opacity var(#{$css-var-prefix}transition);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scss/components/_popover-toast.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | // TODO: Complete this
5 | // https://mdn.github.io/dom-examples/popover-api/toast-popovers/
6 | // --pico-ins-color // success
7 | // --pico-del-color // error
8 | @if map.get($modules, "components/popover-toast") {
9 | #{$parent-selector} [popover] {
10 | border-color: var(#{$css-var-prefix}primary);
11 |
12 | &[data-status="success"] {
13 | border-color: var(--pico-ins-color);
14 | }
15 | &[data-status="failure"] {
16 | border-color: var(--pico-del-color);
17 | }
18 | &:popover-open {
19 | position: absolute;
20 | right: 5px;
21 | bottom: 5px;
22 | inset: unset;
23 | }
24 | &::backdrop {
25 | background: rgba(0, 0, 0, 0.7);
26 | }
27 | button {
28 | margin-top: -10px;
29 | padding: 0;
30 | border: 0 none;
31 | background: transparent;
32 | color: var(#{$css-var-prefix}primary);
33 | font-size: 1.5em;
34 | opacity: 0.8;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/scss/components/_popover.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/popover") {
5 | #{$parent-selector} [popover] {
6 | border-color: var(#{$css-var-prefix}primary);
7 |
8 | &:popover-open {
9 | margin: auto auto;
10 | }
11 |
12 | &::backdrop {
13 | backdrop-filter: blur(3px);
14 | background: rgba(0, 0, 0, 0.7);
15 | }
16 | button {
17 | margin-top: -10px;
18 | padding: 0;
19 | border: 0 none;
20 | background: transparent;
21 | color: var(#{$css-var-prefix}primary);
22 | font-size: 1.5em;
23 | opacity: 0.8;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/scss/components/_progress.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/progress") {
5 | /**
6 | * Progress
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // 1. Add the correct display in Edge 18- and IE
15 | // 2. Add the correct vertical alignment in Chrome, Edge, and Firefox
16 | #{$parent-selector} progress {
17 | display: inline-block; // 1
18 | vertical-align: baseline; // 2
19 | }
20 |
21 | // Pico
22 | // ––––––––––––––––––––
23 |
24 | #{$parent-selector} progress {
25 | // Reset the default appearance
26 | -webkit-appearance: none;
27 | -moz-appearance: none;
28 |
29 | // Styles
30 | display: inline-block;
31 | appearance: none;
32 | width: 100%;
33 | height: 0.5rem;
34 | margin-bottom: calc(var(#{$css-var-prefix}spacing) * 0.5);
35 | overflow: hidden;
36 |
37 | // Remove Firefox and Opera border
38 | border: 0;
39 | border-radius: var(#{$css-var-prefix}border-radius);
40 | background-color: var(#{$css-var-prefix}progress-background-color);
41 |
42 | // IE10 uses `color` to set the bar background-color
43 | color: var(#{$css-var-prefix}progress-color);
44 |
45 | &::-webkit-progress-bar {
46 | border-radius: var(#{$css-var-prefix}border-radius);
47 | background: none;
48 | }
49 |
50 | &[value]::-webkit-progress-value {
51 | background-color: var(#{$css-var-prefix}progress-color);
52 |
53 | @if $enable-transitions {
54 | transition: inline-size var(#{$css-var-prefix}transition);
55 | }
56 | }
57 |
58 | &::-moz-progress-bar {
59 | background-color: var(#{$css-var-prefix}progress-color);
60 | }
61 |
62 | // Indeterminate state
63 | @media (prefers-reduced-motion: no-preference) {
64 | &:indeterminate {
65 | background: var(#{$css-var-prefix}progress-background-color)
66 | linear-gradient(
67 | to right,
68 | var(#{$css-var-prefix}progress-color) 30%,
69 | var(#{$css-var-prefix}progress-background-color) 30%
70 | )
71 | top left / 150% 150% no-repeat;
72 | animation: progress-indeterminate 1s linear infinite;
73 |
74 | &[value]::-webkit-progress-value {
75 | background-color: transparent;
76 | }
77 |
78 | &::-moz-progress-bar {
79 | background-color: transparent;
80 | }
81 | }
82 | }
83 | }
84 |
85 | [dir="rtl"] {
86 | @media (prefers-reduced-motion: no-preference) {
87 | #{$parent-selector} progress:indeterminate {
88 | animation-direction: reverse;
89 | }
90 | }
91 | }
92 |
93 | @keyframes progress-indeterminate {
94 | 0% {
95 | background-position: 200% 0;
96 | }
97 |
98 | 100% {
99 | background-position: -200% 0;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/scss/components/_tab-region.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/tab-region") {
5 | /**
6 | * Tab region
7 | * styling help from: https://github.com/picocss/pico/discussions/608
8 | * and his demo: https://gistpreview.github.io/?86c08db6793d078757aa795845c19ed3
9 | */
10 | #{$parent-selector} section[role="region"] {
11 | display: flex;
12 | flex-wrap: wrap;
13 | margin-bottom: 0;
14 |
15 | details {
16 | display: contents;
17 | margin-bottom: 0;
18 | padding-bottom: 0;
19 |
20 | summary {
21 | flex-grow: 1;
22 | order: 0;
23 | margin: 0;
24 | padding: calc(var(#{$css-var-prefix}block-spacing-vertical) * 0.75)
25 | calc(var(#{$css-var-prefix}block-spacing-horizontal) * 1.5);
26 | border-bottom: 1px solid transparent; //(#{$css-var-prefix}primary-background);
27 | background-color: var(#{$css-var-prefix}card-sectioning-background-color);
28 | list-style-type: none;
29 | touch-action: manipulation;
30 | transition: all var(#{$css-var-prefix}transition);
31 |
32 | &:hover {
33 | border-bottom-color: var(#{$css-var-prefix}primary-border);
34 | background-color: var(#{$css-var-prefix}card-background-color);
35 | }
36 |
37 | &::after {
38 | display: none;
39 | // with the - icon instead of chevron
40 | //transform: rotate(0deg);
41 | //background-image: var(#{$css-var-prefix}icon-minus);
42 | //background-position: center;
43 | //background-size: .75em auto;
44 | }
45 | }
46 |
47 | > div {
48 | opacity: 0;
49 | }
50 |
51 | &[open] {
52 | > summary {
53 | background-color: var(#{$css-var-prefix}primary-background);
54 | color: var(#{$css-var-prefix}primary-inverse) !important;
55 |
56 | &:hover {
57 | background-color: var(#{$css-var-prefix}primary-hover-background);
58 | }
59 |
60 | //&::after {
61 | // black chevron icon
62 | // background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
63 | //}
64 | }
65 |
66 | // end for summary, keep here
67 |
68 | > div {
69 | order: 1;
70 | width: 100%;
71 | padding: var(#{$css-var-prefix}spacing);
72 | padding-bottom: 0;
73 | opacity: 1;
74 | transition: opacity var(#{$css-var-prefix}transition);
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/scss/components/_tab.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/tab") {
5 | /**
6 | * Tabs
7 | * styling help from: https://codepen.io/mikestreety/pen/yVNNNm
8 | */
9 |
10 | @if $enable-transitions {
11 | @keyframes showTab {
12 | from {
13 | //transform: translateY(10px);
14 | scale: 0.75;
15 | opacity: 0;
16 | }
17 | to {
18 | //transform: translateY(0);
19 | scale: 1;
20 | opacity: 1;
21 | }
22 | }
23 | }
24 |
25 | #{$parent-selector} [role="tablist"] {
26 | display: flex;
27 | flex-wrap: wrap; // make sure it wraps
28 | padding-bottom: 0;
29 |
30 | > button[role="tab"] {
31 | flex-grow: 1;
32 | order: 1; // Put the labels first
33 | padding: calc(var(#{$css-var-prefix}spacing) * 0.625);
34 | border: 0;
35 | border-bottom: 1px solid var(#{$css-var-prefix}primary-background);
36 | border-bottom-right-radius: 0;
37 | border-bottom-left-radius: 0;
38 | background: transparent;
39 | color: var(#{$css-var-prefix}contrast);
40 | font-weight: bold;
41 | cursor: pointer;
42 |
43 | @if $enable-transitions {
44 | transition: background-color var(#{$css-var-prefix}transition);
45 | }
46 |
47 | &[aria-selected="true"] {
48 | background: var(#{$css-var-prefix}primary-background);
49 | color: var(#{$css-var-prefix}primary-inverse);
50 | }
51 | &:hover {
52 | background: var(#{$css-var-prefix}primary-hover-background);
53 | color: var(#{$css-var-prefix}primary-inverse);
54 | }
55 | }
56 |
57 | [role="tabpanel"] {
58 | flex-grow: 1;
59 | order: 99; // Put the tabs last
60 | width: 100%;
61 |
62 | //calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 2)
63 | //var(#{$css-var-prefix}form-element-spacing-horizontal);
64 | padding: var(--pico-spacing) calc(var(--pico-spacing) * 0.75);
65 | background: var(
66 | #{$css-var-prefix}accent-section-bg-color
67 | ); //#{$css-var-prefix}card-sectioning-background-color);
68 |
69 | @if $enable-transitions {
70 | animation: var(#{$css-var-prefix}tab-animation) var(#{$css-var-prefix}transition);
71 | }
72 |
73 | &:not([hidden]) {
74 | display: block;
75 | }
76 | &[hidden] {
77 | display: none;
78 | }
79 | > *:last-of-type {
80 | margin-bottom: 0;
81 | }
82 | }
83 | }
84 | @media (max-width: 45em) {
85 | #{$parent-selector} [role="tablist"] {
86 | [role="tabpanel"],
87 | button,
88 | label {
89 | order: initial;
90 | }
91 | label,
92 | [role="tabpanel"],
93 | button {
94 | width: 100%;
95 | margin-top: 0.2rem;
96 | margin-right: 0;
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/scss/components/_timeline.scss:
--------------------------------------------------------------------------------
1 | @use "sass:string";
2 | @use "sass:map";
3 | @use "sass:math";
4 | @use "../settings" as *; // for spacing, breakpoints, and if columns are defined.
5 |
6 | @if map.get($modules, "components/timeline") {
7 | // The actual timeline (the vertical ruler)
8 | #{$parent-selector} .timeline {
9 | position: relative;
10 | max-width: 100%;
11 | margin: 0 auto;
12 |
13 | &::after {
14 | position: absolute;
15 | top: 0;
16 | bottom: 0;
17 | left: 50%;
18 | width: 6px;
19 | margin-left: -3px;
20 | background-color: var(#{$css-var-prefix}timeline-line-color);
21 | content: "";
22 | }
23 |
24 | // Point around content
25 | > .point {
26 | position: relative;
27 | width: 50%;
28 | padding: 10px 25px;
29 | background-color: inherit;
30 |
31 | // circle
32 | &::after {
33 | z-index: 1;
34 | position: absolute;
35 | top: 13px;
36 | right: -13px;
37 | width: 25px;
38 | height: 25px;
39 | border: 4px solid var(#{$css-var-prefix}timeline-dot-border-color);
40 | border-radius: 50%;
41 | background-color: var(#{$css-var-prefix}timeline-dot-background-color);
42 | content: "";
43 | }
44 |
45 | // Place the container to the left
46 | &.left {
47 | left: 0;
48 |
49 | &::before {
50 | z-index: 1;
51 | position: absolute;
52 | top: 15px;
53 | right: 15px;
54 | width: 0;
55 | height: 0;
56 | border: medium solid var(#{$css-var-prefix}timeline-arrow-color);
57 | border-width: 10px 0 10px 10px;
58 | border-color: transparent
59 | transparent
60 | transparent
61 | var(#{$css-var-prefix}timeline-arrow-color);
62 | content: " ";
63 | }
64 | }
65 |
66 | // Place the container to the right
67 | &.right {
68 | left: 50%;
69 |
70 | &::before {
71 | z-index: 1;
72 | position: absolute;
73 | top: 15px;
74 | left: 15px;
75 | width: 0;
76 | height: 0;
77 | border: medium solid var(#{$css-var-prefix}timeline-arrow-color);
78 | border-width: 10px 10px 10px 0;
79 | border-color: transparent
80 | var(#{$css-var-prefix}timeline-arrow-color)
81 | transparent
82 | transparent;
83 | content: " ";
84 | }
85 |
86 | &::after {
87 | left: -13px;
88 | }
89 | }
90 | }
91 | }
92 | // Media queries - Responsive timeline on screens less than 600px wide
93 | @media screen and (max-width: 600px) {
94 | // Place the timelime to the left
95 | #{$parent-selector} .timeline {
96 | // the line
97 | &::after {
98 | left: 13px;
99 | }
100 |
101 | // after = dot
102 | // before = arrow
103 | > .point {
104 | // Full-width containers
105 | width: 100%;
106 | padding-right: 25px;
107 | padding-left: 40px;
108 |
109 | // Make all right containers behave like the left ones
110 | &.right {
111 | left: 0;
112 | }
113 |
114 | // after = dot
115 | &::after,
116 | &.left::after,
117 | &.right::after {
118 | left: 0;
119 | }
120 |
121 | // before = arrow
122 | // Make sure that all arrows are pointing leftwards
123 | // &::before,
124 | &.left::before,
125 | &.right::before {
126 | //left: 60px;
127 | top: 16px;
128 | left: 30px;
129 | border-width: 10px 10px 10px 0;
130 | border-color: transparent var(#{$css-var-prefix}timeline-arrow-color) transparent
131 | transparent;
132 | }
133 | }
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/scss/components/_tooltip.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "components/tooltip") {
5 | /**
6 | * Tooltip ([data-tooltip])
7 | */
8 |
9 | #{$parent-selector} [data-tooltip] {
10 | position: relative;
11 |
12 | &:not(:has(a, button, input, [role="button"])):not(a, button, input, [role="button"]) {
13 | border-bottom: 1px dotted;
14 | text-decoration: none;
15 | cursor: help;
16 | }
17 |
18 | &[data-placement="top"]::before,
19 | &[data-placement="top"]::after,
20 | &::before,
21 | &::after {
22 | display: inline-block;
23 | z-index: 99;
24 | position: absolute;
25 | bottom: 100%;
26 | left: 50%;
27 | width: max-content;
28 | max-width: 250px;
29 | padding: 0.25rem 0.5rem;
30 | overflow: hidden;
31 | transform: translate(-50%, -0.25rem);
32 | border-radius: var(#{$css-var-prefix}border-radius);
33 | background: var(#{$css-var-prefix}tooltip-background-color);
34 | content: attr(data-tooltip);
35 | color: var(#{$css-var-prefix}tooltip-color);
36 | font-style: normal;
37 | font-weight: var(#{$css-var-prefix}font-weight);
38 | font-size: 0.875rem;
39 | text-align: center;
40 | text-decoration: none;
41 | text-overflow: ellipsis;
42 | white-space: normal;
43 | opacity: 0;
44 | pointer-events: none;
45 | }
46 |
47 | // Caret
48 | &[data-placement="top"]::after,
49 | &::after {
50 | width: 0;
51 | padding: 0;
52 | transform: translate(-50%, 0rem);
53 | border-top: 0.3rem solid;
54 | border-right: 0.3rem solid transparent;
55 | border-left: 0.3rem solid transparent;
56 | border-radius: 0;
57 | background-color: transparent;
58 | content: "";
59 | color: var(#{$css-var-prefix}tooltip-background-color);
60 | }
61 |
62 | &[data-placement="bottom"] {
63 | &::before,
64 | &::after {
65 | top: 100%;
66 | bottom: auto;
67 | transform: translate(-50%, 0.25rem);
68 | }
69 |
70 | &:after {
71 | transform: translate(-50%, -0.3rem);
72 | border: 0.3rem solid transparent;
73 | border-bottom: 0.3rem solid;
74 | }
75 | }
76 |
77 | &[data-placement="left"] {
78 | &::before,
79 | &::after {
80 | top: 50%;
81 | right: 100%;
82 | bottom: auto;
83 | left: auto;
84 | transform: translate(-0.25rem, -50%);
85 | }
86 |
87 | &:after {
88 | transform: translate(0.3rem, -50%);
89 | border: 0.3rem solid transparent;
90 | border-left: 0.3rem solid;
91 | }
92 | }
93 |
94 | &[data-placement="right"] {
95 | &::before,
96 | &::after {
97 | top: 50%;
98 | right: auto;
99 | bottom: auto;
100 | left: 100%;
101 | transform: translate(0.25rem, -50%);
102 | }
103 |
104 | &:after {
105 | transform: translate(-0.3rem, -50%);
106 | border: 0.3rem solid transparent;
107 | border-right: 0.3rem solid;
108 | }
109 | }
110 |
111 | // Display
112 | &:focus,
113 | &:hover {
114 | &::before,
115 | &::after {
116 | opacity: 1;
117 | }
118 | }
119 |
120 | @if $enable-transitions {
121 | // Animations, excluding touch devices
122 | @media (hover: hover) and (pointer: fine) {
123 | // Default (top)
124 | &:focus,
125 | &:hover {
126 | &::before,
127 | &::after {
128 | #{$css-var-prefix}tooltip-slide-to: translate(-50%, -0.25rem);
129 | transform: translate(-50%, 0.75rem);
130 | animation-duration: 0.2s;
131 | animation-fill-mode: forwards;
132 | animation-name: tooltip-slide;
133 | opacity: 0;
134 | }
135 |
136 | &::after {
137 | #{$css-var-prefix}tooltip-caret-slide-to: translate(-50%, 0rem);
138 | transform: translate(-50%, -0.25rem);
139 | animation-name: tooltip-caret-slide;
140 | }
141 | }
142 |
143 | // Bottom
144 | &[data-placement="bottom"] {
145 | &:focus,
146 | &:hover {
147 | &::before,
148 | &::after {
149 | #{$css-var-prefix}tooltip-slide-to: translate(-50%, 0.25rem);
150 | transform: translate(-50%, -0.75rem);
151 | animation-name: tooltip-slide;
152 | }
153 |
154 | &::after {
155 | #{$css-var-prefix}tooltip-caret-slide-to: translate(-50%, -0.3rem);
156 | transform: translate(-50%, -0.5rem);
157 | animation-name: tooltip-caret-slide;
158 | }
159 | }
160 | }
161 |
162 | // Left
163 | &[data-placement="left"] {
164 | &:focus,
165 | &:hover {
166 | &::before,
167 | &::after {
168 | #{$css-var-prefix}tooltip-slide-to: translate(-0.25rem, -50%);
169 | transform: translate(0.75rem, -50%);
170 | animation-name: tooltip-slide;
171 | }
172 |
173 | &::after {
174 | #{$css-var-prefix}tooltip-caret-slide-to: translate(0.3rem, -50%);
175 | transform: translate(0.05rem, -50%);
176 | animation-name: tooltip-caret-slide;
177 | }
178 | }
179 | }
180 |
181 | // Right
182 | &[data-placement="right"] {
183 | &:focus,
184 | &:hover {
185 | &::before,
186 | &::after {
187 | #{$css-var-prefix}tooltip-slide-to: translate(0.25rem, -50%);
188 | transform: translate(-0.75rem, -50%);
189 | animation-name: tooltip-slide;
190 | }
191 |
192 | &::after {
193 | #{$css-var-prefix}tooltip-caret-slide-to: translate(-0.3rem, -50%);
194 | transform: translate(-0.05rem, -50%);
195 | animation-name: tooltip-caret-slide;
196 | }
197 | }
198 | }
199 | }
200 |
201 | @keyframes tooltip-slide {
202 | to {
203 | transform: var(#{$css-var-prefix}tooltip-slide-to);
204 | opacity: 1;
205 | }
206 | }
207 |
208 | @keyframes tooltip-caret-slide {
209 | 50% {
210 | opacity: 0;
211 | }
212 |
213 | to {
214 | transform: var(#{$css-var-prefix}tooltip-caret-slide-to);
215 | opacity: 1;
216 | }
217 | }
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/scss/content/_button.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/button") {
5 | /**
6 | * Button
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // 1. Change the font styles in all browsers
15 | // 2. Remove the margin on controls in Safari
16 | // 3. Show the overflow in Edge
17 | #{$parent-selector} button {
18 | margin: 0; // 2
19 | overflow: visible; // 3
20 | font-family: inherit; // 1
21 | text-transform: none; // 1
22 | }
23 |
24 | // Correct the inability to style buttons in iOS and Safari
25 | #{$parent-selector} button,
26 | #{$parent-selector} [type="submit"],
27 | #{$parent-selector} [type="reset"],
28 | #{$parent-selector} [type="button"] {
29 | -webkit-appearance: button;
30 | }
31 |
32 | // Pico
33 | // ––––––––––––––––––––
34 |
35 | #{$parent-selector} button,
36 | #{$parent-selector} [type="submit"],
37 | #{$parent-selector} [type="reset"],
38 | #{$parent-selector} [type="button"],
39 | #{$parent-selector} [type="file"]::file-selector-button,
40 | #{$parent-selector} [role="button"] {
41 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}primary-background);
42 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}primary-border);
43 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary-inverse);
44 | #{$css-var-prefix}box-shadow: var(#{$css-var-prefix}button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));
45 | padding: var(#{$css-var-prefix}form-element-spacing-vertical)
46 | var(#{$css-var-prefix}form-element-spacing-horizontal);
47 | border: var(#{$css-var-prefix}border-width) solid var(#{$css-var-prefix}border-color);
48 | border-radius: var(#{$css-var-prefix}border-radius);
49 | outline: none;
50 | background-color: var(#{$css-var-prefix}background-color);
51 | box-shadow: var(#{$css-var-prefix}box-shadow);
52 | color: var(#{$css-var-prefix}color);
53 | font-weight: var(#{$css-var-prefix}font-weight);
54 | font-size: 1rem;
55 | line-height: var(#{$css-var-prefix}line-height);
56 | text-align: center;
57 | text-decoration: none;
58 | cursor: pointer;
59 | user-select: none;
60 |
61 | @if $enable-transitions {
62 | transition:
63 | background-color var(#{$css-var-prefix}transition),
64 | border-color var(#{$css-var-prefix}transition),
65 | color var(#{$css-var-prefix}transition),
66 | box-shadow var(#{$css-var-prefix}transition);
67 | }
68 |
69 | &:is([aria-current]:not([aria-current="false"])),
70 | &:is(:hover, :active, :focus) {
71 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}primary-hover-background);
72 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}primary-hover-border);
73 | #{$css-var-prefix}box-shadow: var(
74 | #{$css-var-prefix}button-hover-box-shadow,
75 | 0 0 0 rgba(0, 0, 0, 0)
76 | );
77 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary-inverse);
78 | }
79 |
80 | &:focus,
81 | &:is([aria-current]:not([aria-current="false"])):focus {
82 | #{$css-var-prefix}box-shadow:
83 | var(#{$css-var-prefix}button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),
84 | 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}primary-focus);
85 | }
86 | }
87 |
88 | #{$parent-selector} [type="submit"],
89 | #{$parent-selector} [type="reset"],
90 | #{$parent-selector} [type="button"] {
91 | margin-bottom: var(#{$css-var-prefix}spacing);
92 | }
93 |
94 | // .secondary, .contrast & .outline
95 | @if $enable-classes {
96 | // Secondary
97 | #{$parent-selector} :is(button, [type="submit"], [type="button"], [role="button"]).secondary,
98 | #{$parent-selector} [type="reset"],
99 | #{$parent-selector} [type="file"]::file-selector-button {
100 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}secondary-background);
101 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-border);
102 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-inverse);
103 | cursor: pointer;
104 |
105 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
106 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}secondary-hover-background);
107 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-hover-border);
108 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-inverse);
109 | }
110 |
111 | &:focus,
112 | &:is([aria-current]:not([aria-current="false"])):focus {
113 | #{$css-var-prefix}box-shadow:
114 | var(#{$css-var-prefix}button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),
115 | 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}secondary-focus);
116 | }
117 | }
118 |
119 | // Contrast
120 | #{$parent-selector} :is(button, [type="submit"], [type="button"], [role="button"]).contrast {
121 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}contrast-background);
122 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}contrast-border);
123 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast-inverse);
124 |
125 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
126 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}contrast-hover-background);
127 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}contrast-hover-border);
128 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast-inverse);
129 | }
130 |
131 | &:focus,
132 | &:is([aria-current]:not([aria-current="false"])):focus {
133 | #{$css-var-prefix}box-shadow:
134 | var(#{$css-var-prefix}button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),
135 | 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}contrast-focus);
136 | }
137 | }
138 |
139 | // Outline (primary)
140 | #{$parent-selector} :is(button, [type="submit"], [type="button"], [role="button"]).outline,
141 | [type="reset"].outline {
142 | #{$css-var-prefix}background-color: transparent;
143 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary);
144 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}primary);
145 |
146 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
147 | #{$css-var-prefix}background-color: transparent;
148 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary-hover);
149 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}primary-hover);
150 | }
151 | }
152 |
153 | // Outline (secondary)
154 | #{$parent-selector}
155 | :is(button, [type="submit"], [type="button"], [role="button"]).outline.secondary,
156 | [type="reset"].outline {
157 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary);
158 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary);
159 |
160 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
161 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-hover);
162 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-hover);
163 | }
164 | }
165 |
166 | // Outline (contrast)
167 | #{$parent-selector}
168 | :is(button, [type="submit"], [type="button"], [role="button"]).outline.contrast {
169 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast);
170 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}contrast);
171 |
172 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
173 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast-hover);
174 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}contrast-hover);
175 | }
176 | }
177 | } @else {
178 | // Secondary button without .class
179 | #{$parent-selector} [type="reset"],
180 | #{$parent-selector} [type="file"]::file-selector-button {
181 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}secondary-background);
182 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-border);
183 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-inverse);
184 | cursor: pointer;
185 |
186 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
187 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}secondary-hover-background);
188 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-hover-border);
189 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-inverse);
190 | }
191 |
192 | &:focus {
193 | #{$css-var-prefix}box-shadow:
194 | var(#{$css-var-prefix}button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),
195 | 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}secondary-focus);
196 | }
197 | }
198 | }
199 |
200 | // Button [disabled]
201 | #{$parent-selector}
202 | :where(button, [type="submit"], [type="reset"], [type="button"], [role="button"])[disabled],
203 | #{$parent-selector}
204 | :where(fieldset[disabled])
205 | :is(button, [type="submit"], [type="button"], [type="reset"], [role="button"]) {
206 | opacity: 0.5;
207 | pointer-events: none;
208 | }
209 |
210 | // Ghost buttons
211 | // See: https://github.com/Yohn/PicoCSS/discussions/29
212 | @if $enable-classes {
213 | $btn-elements: 'button, [type="button"], [type="submit"], [type="reset"], [role="button"]';
214 | $btn-is: ':hover, :focus, :active, [aria-current]:not([aria-current="false"])';
215 | /* Base ghost button style */
216 | #{$parent-selector} :is(#{$btn-elements}).ghost {
217 | #{$css-var-prefix}background-color: transparent;
218 | #{$css-var-prefix}border-color: transparent;
219 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary);
220 |
221 | &:is(#{$btn-is}) {
222 | #{$css-var-prefix}background-color: transparent;
223 | #{$css-var-prefix}border-color: transparent;
224 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary-hover);
225 | }
226 | }
227 |
228 | /* Secondary ghost button */
229 | #{$parent-selector} :is(#{$btn-elements}).ghost.secondary {
230 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary);
231 | &:is(#{$btn-is}) {
232 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-hover);
233 | }
234 | }
235 |
236 | /* Contrast ghost button */
237 | #{$parent-selector} :is(#{$btn-elements}).ghost.contrast {
238 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast);
239 | &:is(#{$btn-is}) {
240 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast-hover);
241 | }
242 | }
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/scss/content/_code.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/code") {
5 | /**
6 | * Code
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // 1. Correct the inheritance and scaling of font size in all browsers
15 | // 2. Correct the odd `em` font sizing in all browsers
16 | #{$parent-selector} pre,
17 | #{$parent-selector} code,
18 | #{$parent-selector} kbd,
19 | #{$parent-selector} samp {
20 | font-size: 0.875em; // 2
21 | font-family: var(#{$css-var-prefix}font-family); // 1
22 | }
23 |
24 | #{$parent-selector} pre code {
25 | font-size: inherit;
26 | font-family: inherit;
27 | }
28 |
29 | // Prevent overflow of the container in all browsers (opinionated)
30 | #{$parent-selector} pre {
31 | -ms-overflow-style: scrollbar;
32 | overflow: auto;
33 | }
34 |
35 | // Pico
36 | // ––––––––––––––––––––
37 |
38 | #{$parent-selector} pre,
39 | #{$parent-selector} code,
40 | #{$parent-selector} kbd {
41 | border-radius: var(#{$css-var-prefix}border-radius);
42 | background: var(#{$css-var-prefix}code-background-color);
43 | color: var(#{$css-var-prefix}code-color);
44 | font-weight: var(#{$css-var-prefix}font-weight);
45 | line-height: initial;
46 | }
47 |
48 | #{$parent-selector} code,
49 | #{$parent-selector} kbd {
50 | display: inline-block;
51 | padding: 0.125rem 0.375rem;
52 | vertical-align: middle;
53 | }
54 |
55 | #{$parent-selector} pre {
56 | display: block;
57 | margin-bottom: var(#{$css-var-prefix}spacing);
58 |
59 | > code {
60 | display: block;
61 | padding: var(#{$css-var-prefix}spacing);
62 | overflow-x: auto;
63 | background: none;
64 | line-height: var(#{$css-var-prefix}line-height);
65 | }
66 | }
67 |
68 | // kbd
69 | #{$parent-selector} kbd {
70 | background-color: var(#{$css-var-prefix}code-kbd-background-color);
71 | color: var(#{$css-var-prefix}code-kbd-color);
72 | vertical-align: baseline;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/scss/content/_embedded.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/embedded") {
5 | /**
6 | * Embedded content
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // Change the alignment on media elements in all browsers (opinionated)
15 | #{$parent-selector} :where(audio, canvas, iframe, img, svg, video) {
16 | vertical-align: middle;
17 | }
18 |
19 | // Add the correct display in IE 9-
20 | #{$parent-selector} audio,
21 | #{$parent-selector} video {
22 | display: inline-block;
23 | }
24 |
25 | // Add the correct display in iOS 4-7
26 | #{$parent-selector} audio:not([controls]) {
27 | display: none;
28 | height: 0;
29 | }
30 |
31 | // Remove the border on iframes in all browsers (opinionated)
32 | #{$parent-selector} :where(iframe) {
33 | border-style: none;
34 | }
35 |
36 | // 1. Remove the border on images inside links in IE 10.
37 | // 2. Responsive by default
38 | #{$parent-selector} img {
39 | max-width: 100%; // 2
40 | height: auto; // 2
41 | border-style: none; // 1
42 | }
43 |
44 | // Change the fill color to match the text color in all browsers (opinionated)
45 | #{$parent-selector} :where(svg:not([fill])) {
46 | fill: currentColor;
47 | }
48 |
49 | // Hide the overflow in IE
50 | #{$parent-selector} svg:not(:root),
51 | #{$parent-selector} svg:not(:host) {
52 | overflow: hidden;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/scss/content/_figure.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/figure") {
5 | /**
6 | * Figure
7 | */
8 |
9 | #{$parent-selector} figure {
10 | display: block;
11 | margin: 0;
12 | padding: 0;
13 |
14 | figcaption {
15 | padding: calc(var(#{$css-var-prefix}spacing) * 0.5) 0;
16 | color: var(#{$css-var-prefix}muted-color);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/scss/content/_link.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/link") {
5 | /**
6 | * Link
7 | */
8 |
9 | #{$parent-selector} :where(a:not([role="button"])),
10 | #{$parent-selector} [role="link"] {
11 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary);
12 | #{$css-var-prefix}background-color: transparent;
13 | #{$css-var-prefix}underline: var(#{$css-var-prefix}primary-underline);
14 | outline: none;
15 | background-color: var(#{$css-var-prefix}background-color); // 1
16 | color: var(#{$css-var-prefix}color);
17 | text-decoration: var(#{$css-var-prefix}text-decoration);
18 | text-decoration-color: var(#{$css-var-prefix}underline);
19 | text-underline-offset: 0.125em;
20 |
21 | @if $enable-transitions {
22 | transition:
23 | background-color var(#{$css-var-prefix}transition),
24 | color var(#{$css-var-prefix}transition),
25 | text-decoration var(#{$css-var-prefix}transition),
26 | box-shadow var(#{$css-var-prefix}transition);
27 | }
28 |
29 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
30 | #{$css-var-prefix}color: var(#{$css-var-prefix}primary-hover);
31 | #{$css-var-prefix}underline: var(#{$css-var-prefix}primary-hover-underline);
32 | #{$css-var-prefix}text-decoration: underline;
33 | }
34 |
35 | &:focus-visible {
36 | box-shadow: 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}primary-focus);
37 | }
38 |
39 | @if $enable-classes {
40 | // Secondary
41 | &.secondary {
42 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary);
43 | #{$css-var-prefix}underline: var(#{$css-var-prefix}secondary-underline);
44 |
45 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
46 | #{$css-var-prefix}color: var(#{$css-var-prefix}secondary-hover);
47 | #{$css-var-prefix}underline: var(#{$css-var-prefix}secondary-hover-underline);
48 | }
49 | }
50 |
51 | // Contrast
52 | &.contrast {
53 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast);
54 | #{$css-var-prefix}underline: var(#{$css-var-prefix}contrast-underline);
55 |
56 | &:is([aria-current]:not([aria-current="false"]), :hover, :active, :focus) {
57 | #{$css-var-prefix}color: var(#{$css-var-prefix}contrast-hover);
58 | #{$css-var-prefix}underline: var(#{$css-var-prefix}contrast-hover-underline);
59 | }
60 | }
61 | }
62 | }
63 |
64 | #{$parent-selector} a {
65 | &[role="button"] {
66 | display: inline-block;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/scss/content/_misc.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/misc") {
5 | /**
6 | * Misc
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // 1. Correct the inheritance of border color in Firefox
15 | // 2. Add the correct box sizing in Firefox
16 | #{$parent-selector} hr {
17 | height: 0; // 2
18 | margin: var(#{$css-var-prefix}typography-spacing-vertical) 0;
19 | border: 0;
20 | border-top: 1px solid var(#{$css-var-prefix}muted-border-color);
21 | color: inherit; // 1
22 | }
23 |
24 | // Add the correct display in IE 10+
25 | #{$parent-selector} [hidden],
26 | #{$parent-selector} template {
27 | @if $enable-important {
28 | display: none !important;
29 | } @else {
30 | display: none;
31 | }
32 | }
33 |
34 | // Add the correct display in IE 9-
35 | #{$parent-selector} canvas {
36 | display: inline-block;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/scss/content/_table.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/table") {
5 | /**
6 | * Table
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // 1. Collapse border spacing in all browsers (opinionated)
15 | // 2. Remove text indentation from table contents in Chrome, Edge, and Safari
16 | #{$parent-selector} :where(table) {
17 | width: 100%;
18 | border-collapse: collapse; // 1
19 | border-spacing: 0;
20 | text-indent: 0; // 2
21 | }
22 |
23 | // Pico
24 | // ––––––––––––––––––––
25 |
26 | // Cells
27 | #{$parent-selector} table th,
28 | #{$parent-selector} table td {
29 | padding: calc(var(#{$css-var-prefix}spacing) / 2) var(#{$css-var-prefix}spacing);
30 | border-bottom: var(#{$css-var-prefix}border-width)
31 | solid
32 | var(#{$css-var-prefix}table-border-color);
33 | background-color: var(#{$css-var-prefix}background-color);
34 | color: var(#{$css-var-prefix}color);
35 | font-weight: var(#{$css-var-prefix}font-weight);
36 | text-align: left;
37 | text-align: start;
38 | }
39 |
40 | #{$parent-selector} table > caption {
41 | margin-block: calc(var(#{$css-var-prefix}block-spacing-vertical) * 0.5);
42 | padding: calc(var(#{$css-var-prefix}spacing) / 2) var(#{$css-var-prefix}spacing);
43 | background-color: var(#{$css-var-prefix}table-row-stripped-background-color);
44 | color: var(#{$css-var-prefix}h3-color);
45 | font-weight: var(#{$css-var-prefix}font-weight);
46 | font-size: 1.25rem;
47 | text-align: center;
48 | }
49 |
50 | // Footer
51 | #{$parent-selector} table > tfoot {
52 | th,
53 | td {
54 | border-top: var(#{$css-var-prefix}border-width)
55 | solid
56 | var(#{$css-var-prefix}table-border-color);
57 | border-bottom: 0;
58 | }
59 | }
60 |
61 | #{$parent-selector} table > caption {
62 | margin-block: calc(var(#{$css-var-prefix}block-spacing-vertical) * 0.5);
63 | background-color: var(#{$css-var-prefix}table-row-stripped-background-color);
64 | color: var(#{$css-var-prefix}h3-color);
65 | font-weight: var(#{$css-var-prefix}font-weight);
66 | font-size: 1.25rem;
67 | text-align: center;
68 | }
69 |
70 | // Striped
71 | @if enable-classes {
72 | /* clean-css ignore:start */
73 | #{$parent-selector} table.striped {
74 | tbody tr:nth-child(odd of :not([hidden])) th,
75 | tbody tr:nth-child(odd of :not([hidden])) td {
76 | background-color: var(#{$css-var-prefix}table-row-stripped-background-color);
77 | }
78 | }
79 | /* clean-css ignore:end */
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/scss/content/_typography.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "content/typography") {
5 | /**
6 | * Typography
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // Add the correct font weight in Chrome, Edge, and Safari
15 | #{$parent-selector} b,
16 | #{$parent-selector} strong {
17 | font-weight: bolder;
18 | }
19 |
20 | // Prevent `sub` and `sup` elements from affecting the line height in all browsers
21 | #{$parent-selector} sub,
22 | #{$parent-selector} sup {
23 | position: relative;
24 | font-size: 0.75em;
25 | line-height: 0;
26 | vertical-align: baseline;
27 | }
28 | #{$parent-selector} sub {
29 | bottom: -0.25em;
30 | }
31 | #{$parent-selector} sup {
32 | top: -0.5em;
33 | }
34 |
35 | // Pico
36 | // ––––––––––––––––––––
37 |
38 | #{$parent-selector} address,
39 | #{$parent-selector} blockquote,
40 | #{$parent-selector} dl,
41 | #{$parent-selector} ol,
42 | #{$parent-selector} p,
43 | #{$parent-selector} pre,
44 | #{$parent-selector} table,
45 | #{$parent-selector} ul {
46 | margin-top: 0;
47 | margin-bottom: var(#{$css-var-prefix}typography-spacing-vertical);
48 | color: var(#{$css-var-prefix}color);
49 | font-style: normal;
50 | font-weight: var(#{$css-var-prefix}font-weight);
51 | }
52 |
53 | // Headings
54 | #{$parent-selector} h1,
55 | #{$parent-selector} h2,
56 | #{$parent-selector} h3,
57 | #{$parent-selector} h4,
58 | #{$parent-selector} h5,
59 | #{$parent-selector} h6 {
60 | margin-top: 0;
61 | margin-bottom: var(#{$css-var-prefix}typography-spacing-vertical);
62 | color: var(#{$css-var-prefix}color);
63 | font-weight: var(#{$css-var-prefix}font-weight);
64 | font-size: var(#{$css-var-prefix}font-size);
65 | line-height: var(#{$css-var-prefix}line-height);
66 | font-family: var(#{$css-var-prefix}font-family);
67 | }
68 |
69 | #{$parent-selector} h1 {
70 | #{$css-var-prefix}color: var(#{$css-var-prefix}h1-color);
71 | }
72 | #{$parent-selector} h2 {
73 | #{$css-var-prefix}color: var(#{$css-var-prefix}h2-color);
74 | }
75 | #{$parent-selector} h3 {
76 | #{$css-var-prefix}color: var(#{$css-var-prefix}h3-color);
77 | }
78 | #{$parent-selector} h4 {
79 | #{$css-var-prefix}color: var(#{$css-var-prefix}h4-color);
80 | }
81 | #{$parent-selector} h5 {
82 | #{$css-var-prefix}color: var(#{$css-var-prefix}h5-color);
83 | }
84 | #{$parent-selector} h6 {
85 | #{$css-var-prefix}color: var(#{$css-var-prefix}h6-color);
86 | }
87 |
88 | // Margin-top for headings after a block
89 | #{$parent-selector}
90 | :where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) {
91 | ~ :is(h1, h2, h3, h4, h5, h6) {
92 | margin-top: var(#{$css-var-prefix}typography-spacing-top);
93 | }
94 | }
95 |
96 | // Paragraphs
97 | #{$parent-selector} p {
98 | margin-bottom: var(#{$css-var-prefix}typography-spacing-vertical);
99 | }
100 |
101 | // Heading group
102 | #{$parent-selector} hgroup {
103 | margin-bottom: var(#{$css-var-prefix}typography-spacing-vertical);
104 |
105 | > * {
106 | margin-top: 0;
107 | margin-bottom: 0;
108 | }
109 |
110 | > *:not(:first-child):last-child {
111 | #{$css-var-prefix}color: var(#{$css-var-prefix}muted-color);
112 | #{$css-var-prefix}font-weight: unset;
113 | font-size: 1rem;
114 | }
115 | }
116 |
117 | // Lists
118 | #{$parent-selector} :where(ol, ul) {
119 | li {
120 | margin-bottom: calc(var(#{$css-var-prefix}typography-spacing-vertical) * 0.25);
121 | }
122 | }
123 |
124 | // Margin-top for nested lists
125 | // 1. Remove the margin on nested lists in Chrome, Edge, IE, and Safari
126 | #{$parent-selector} :where(dl, ol, ul) :where(dl, ol, ul) {
127 | margin: 0; // 1
128 | margin-top: calc(var(#{$css-var-prefix}typography-spacing-vertical) * 0.25);
129 | }
130 |
131 | #{$parent-selector} ul li {
132 | list-style: square;
133 | }
134 |
135 | // Highlighted text
136 | #{$parent-selector} mark {
137 | padding: 0.125rem 0.25rem;
138 | background-color: var(#{$css-var-prefix}mark-background-color);
139 | color: var(#{$css-var-prefix}mark-color);
140 | vertical-align: baseline;
141 | }
142 |
143 | // Blockquote
144 | #{$parent-selector} blockquote {
145 | display: block;
146 | margin: var(#{$css-var-prefix}typography-spacing-vertical) 0;
147 | padding: var(#{$css-var-prefix}spacing);
148 | border-right: none;
149 | border-left: 0.25rem solid var(#{$css-var-prefix}blockquote-border-color);
150 | border-inline-start: 0.25rem solid var(#{$css-var-prefix}blockquote-border-color);
151 | border-inline-end: none;
152 |
153 | footer {
154 | margin-top: calc(var(#{$css-var-prefix}typography-spacing-vertical) * 0.5);
155 | color: var(#{$css-var-prefix}blockquote-footer-color);
156 | }
157 | }
158 |
159 | // Abbreviations
160 | // 1. Remove underline decoration in Chrome, Edge, IE, Opera, and Safari
161 | #{$parent-selector} abbr[title] {
162 | border-bottom: 1px dotted;
163 | text-decoration: none; // 1
164 | cursor: help;
165 | }
166 |
167 | // Ins
168 | #{$parent-selector} ins {
169 | color: var(#{$css-var-prefix}ins-color);
170 | text-decoration: none;
171 | }
172 |
173 | // del
174 | #{$parent-selector} del {
175 | color: var(#{$css-var-prefix}del-color);
176 | }
177 |
178 | // selection
179 | #{$parent-selector} ::selection {
180 | background-color: var(#{$css-var-prefix}text-selection-color);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/scss/forms/_floating.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "forms/floating") {
5 | $transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
6 |
7 | #{$parent-selector} section[role="form"] {
8 | position: relative;
9 | margin-bottom: 0;
10 |
11 | > input::placeholder,
12 | > textarea::placeholder {
13 | color: rgba(0, 0, 0, 0);
14 | transition: color $transition-fast;
15 | }
16 |
17 | > input:focus::placeholder,
18 | > textarea:focus::placeholder {
19 | color: var(--pico-form-element-placeholder-color);
20 | }
21 |
22 | > input + label,
23 | > textarea + label,
24 | > select + label {
25 | position: absolute;
26 | top: 0.55rem;
27 | left: 0.8rem;
28 | transform: translateY(0%);
29 | background: var(#{$css-var-prefix}form-element-background-color);
30 | color: var(#{$css-var-prefix}form-element-placeholder-color);
31 | cursor: text;
32 | transition: 0.3s ease;
33 | }
34 |
35 | > input:not(:placeholder-shown) + label,
36 | > input:focus + label,
37 | > textarea:not(:placeholder-shown) + label,
38 | > textarea:focus + label,
39 | > select:focus + label,
40 | > select:has(option:checked:not([disabled])) + label {
41 | top: 0;
42 | padding: calc(var(#{$css-var-prefix}spacing) * 0.15)
43 | calc(var(#{$css-var-prefix}spacing) * 0.5);
44 | transform: translateY(-50%) scale(0.8);
45 | color: var(#{$css-var-prefix}form-element-active-border-color);
46 | font-size: var(#{$css-var-prefix}font-size);
47 | line-height: 1.15;
48 | transition: all $transition-fast;
49 | }
50 |
51 | @if map.get($modules, "forms/validation") {
52 | > input:user-invalid:not(:placeholder-shown) + label,
53 | > textarea:user-invalid:not(:placeholder-shown) + label {
54 | color: var(#{$css-var-prefix}form-element-invalid-border-color);
55 | }
56 |
57 | > input:user-valid:not(:placeholder-shown) + label,
58 | > textarea:user-valid:not(:placeholder-shown) + label {
59 | color: var(#{$css-var-prefix}form-element-valid-border-color);
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/scss/forms/_input-color.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | // Wrapper
5 | @mixin color-wrapper {
6 | padding: 0;
7 | }
8 |
9 | // Swatch
10 | @mixin color-swatch {
11 | border: 0;
12 | border-radius: calc(var(#{$css-var-prefix}border-radius) * 0.5);
13 | }
14 |
15 | @if map.get($modules, "forms/input-color") {
16 | /**
17 | * Input type color
18 | */
19 |
20 | #{$parent-selector} [type="color"] {
21 | &::-webkit-color-swatch-wrapper {
22 | @include color-wrapper;
23 | }
24 |
25 | &::-moz-focus-inner {
26 | @include color-wrapper;
27 | }
28 |
29 | &::-webkit-color-swatch {
30 | @include color-swatch;
31 | }
32 |
33 | &::-moz-color-swatch {
34 | @include color-swatch;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/scss/forms/_input-date.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "forms/input-date") {
5 | /**
6 | * Input type datetime
7 | */
8 |
9 | // :not() are needed to add Specificity and avoid !important on padding
10 | #{$parent-selector} input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]) {
11 | &:is([type="date"], [type="datetime-local"], [type="month"], [type="time"], [type="week"]) {
12 | #{$css-var-prefix}icon-position: 0.75rem;
13 | #{$css-var-prefix}icon-width: 1rem;
14 | padding-right: calc(var(#{$css-var-prefix}icon-width) + var(#{$css-var-prefix}icon-position));
15 | background-image: var(#{$css-var-prefix}icon-date);
16 | background-position: center right var(#{$css-var-prefix}icon-position);
17 | background-size: var(#{$css-var-prefix}icon-width) auto;
18 | background-repeat: no-repeat;
19 | }
20 |
21 | // Time
22 | &[type="time"] {
23 | background-image: var(#{$css-var-prefix}icon-time);
24 | }
25 | }
26 |
27 | // Calendar picker
28 | #{$parent-selector} [type="date"],
29 | #{$parent-selector} [type="datetime-local"],
30 | #{$parent-selector} [type="month"],
31 | #{$parent-selector} [type="time"],
32 | #{$parent-selector} [type="week"] {
33 | &::-webkit-calendar-picker-indicator {
34 | width: var(#{$css-var-prefix}icon-width);
35 | margin-right: calc(var(#{$css-var-prefix}icon-width) * -1);
36 | margin-left: var(#{$css-var-prefix}icon-position);
37 | opacity: 0;
38 | }
39 | }
40 |
41 | // Calendar icons are hidden in Firefox
42 | @if $enable-important {
43 | @-moz-document url-prefix() {
44 | #{$parent-selector} [type="date"],
45 | #{$parent-selector} [type="datetime-local"],
46 | #{$parent-selector} [type="month"],
47 | #{$parent-selector} [type="time"],
48 | #{$parent-selector} [type="week"] {
49 | padding-right: var(#{$css-var-prefix}form-element-spacing-horizontal) !important;
50 | background-image: none !important;
51 | }
52 | }
53 | }
54 |
55 | [dir="rtl"]
56 | #{$parent-selector}
57 | :is([type="date"], [type="datetime-local"], [type="month"], [type="time"], [type="week"]) {
58 | text-align: right;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/scss/forms/_input-file.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "forms/input-file") {
5 | /**
6 | * Input type file
7 | */
8 |
9 | // 1. Hack to display the outline on the focused file selector button
10 | // with the forced overflow hidden on the input[type="file"] element.
11 | #{$parent-selector} [type="file"] {
12 | #{$css-var-prefix}color: var(#{$css-var-prefix}muted-color);
13 | margin-left: calc(var(#{$css-var-prefix}outline-width) * -1); // 1
14 | padding: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5) 0;
15 | padding-left: var(#{$css-var-prefix}outline-width); // 1
16 | border: 0;
17 | border-radius: 0;
18 | background: none;
19 |
20 | &::file-selector-button {
21 | margin-right: calc(var(#{$css-var-prefix}spacing) / 2);
22 | padding: calc(var(#{$css-var-prefix}form-element-spacing-vertical) * 0.5)
23 | var(#{$css-var-prefix}form-element-spacing-horizontal);
24 | }
25 |
26 | &:is(:hover, :active, :focus) {
27 | &::file-selector-button {
28 | #{$css-var-prefix}background-color: var(#{$css-var-prefix}secondary-hover-background);
29 | #{$css-var-prefix}border-color: var(#{$css-var-prefix}secondary-hover-border);
30 | }
31 | }
32 |
33 | &:focus {
34 | &::file-selector-button {
35 | #{$css-var-prefix}box-shadow:
36 | var(#{$css-var-prefix}button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),
37 | 0 0 0 var(#{$css-var-prefix}outline-width) var(#{$css-var-prefix}secondary-focus);
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/scss/forms/_input-range.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | // Config
5 | $height-track: 0.375rem;
6 | $height-thumb: 1.25rem;
7 | $border-thumb: 2px;
8 |
9 | // Slider Track
10 | @mixin slider-track {
11 | width: 100%;
12 | height: $height-track;
13 | border-radius: var(#{$css-var-prefix}border-radius);
14 | background-color: var(#{$css-var-prefix}range-border-color);
15 |
16 | @if $enable-transitions {
17 | transition:
18 | background-color var(#{$css-var-prefix}transition),
19 | box-shadow var(#{$css-var-prefix}transition);
20 | }
21 | }
22 |
23 | // Slider Thumb
24 | @mixin slider-thumb {
25 | -webkit-appearance: none;
26 | width: $height-thumb;
27 | height: $height-thumb;
28 | margin-top: #{(-($height-thumb * 0.5) + ($height-track * 0.5))};
29 | border: $border-thumb solid var(#{$css-var-prefix}range-thumb-border-color);
30 | border-radius: 50%;
31 | background-color: var(#{$css-var-prefix}range-thumb-color);
32 | cursor: pointer;
33 |
34 | @if $enable-transitions {
35 | transition:
36 | background-color var(#{$css-var-prefix}transition),
37 | transform var(#{$css-var-prefix}transition);
38 | }
39 | }
40 |
41 | @if map.get($modules, "forms/input-range") {
42 | /**
43 | * Input type range
44 | */
45 |
46 | #{$parent-selector} [type="range"] {
47 | // Styles
48 | -webkit-appearance: none;
49 | -moz-appearance: none;
50 | appearance: none;
51 | width: 100%;
52 | height: $height-thumb;
53 | background: none;
54 |
55 | &::-webkit-slider-runnable-track {
56 | @include slider-track;
57 | }
58 |
59 | &::-moz-range-track {
60 | @include slider-track;
61 | }
62 |
63 | &::-ms-track {
64 | @include slider-track;
65 | }
66 |
67 | &::-webkit-slider-thumb {
68 | @include slider-thumb;
69 | }
70 |
71 | &::-moz-range-thumb {
72 | @include slider-thumb;
73 | }
74 |
75 | &::-ms-thumb {
76 | @include slider-thumb;
77 | }
78 |
79 | &:active,
80 | &:focus-within {
81 | #{$css-var-prefix}range-border-color: var(#{$css-var-prefix}range-active-border-color);
82 | #{$css-var-prefix}range-thumb-color: var(#{$css-var-prefix}range-thumb-active-color);
83 | }
84 |
85 | &:active {
86 | // Slider Thumb
87 | &::-webkit-slider-thumb {
88 | transform: scale(1.25);
89 | }
90 |
91 | &::-moz-range-thumb {
92 | transform: scale(1.25);
93 | }
94 |
95 | &::-ms-thumb {
96 | transform: scale(1.25);
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/scss/forms/_input-search.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "forms/input-search") {
5 | /**
6 | * Input type search
7 | */
8 |
9 | // :not() are needed to add Specificity and avoid !important on padding
10 | #{$parent-selector} input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]) {
11 | &[type="search"] {
12 | padding-inline-start: calc(var(#{$css-var-prefix}form-element-spacing-horizontal) + 1.75rem);
13 | background-image: var(#{$css-var-prefix}icon-search);
14 | background-position: center
15 | left
16 | calc(var(#{$css-var-prefix}form-element-spacing-horizontal) + 0.125rem);
17 | background-size: 1rem auto;
18 | background-repeat: no-repeat;
19 |
20 | &[aria-invalid] {
21 | @if $enable-important {
22 | padding-inline-start: calc(
23 | var(#{$css-var-prefix}form-element-spacing-horizontal) + 1.75rem
24 | ) !important;
25 | } @else {
26 | padding-inline-start: calc(
27 | var(#{$css-var-prefix}form-element-spacing-horizontal) + 1.75rem
28 | );
29 | }
30 | background-position:
31 | center left 1.125rem,
32 | center right 0.75rem;
33 | }
34 |
35 | &[aria-invalid="false"] {
36 | background-image: var(#{$css-var-prefix}icon-search), var(#{$css-var-prefix}icon-valid);
37 | }
38 |
39 | &[aria-invalid="true"] {
40 | background-image: var(#{$css-var-prefix}icon-search), var(#{$css-var-prefix}icon-invalid);
41 | }
42 | }
43 | }
44 |
45 | [dir="rtl"] {
46 | #{$parent-selector} :where(input) {
47 | &:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]) {
48 | &[type="search"] {
49 | background-position: center right 1.125rem;
50 |
51 | &[aria-invalid] {
52 | background-position:
53 | center right 1.125rem,
54 | center left 0.75rem;
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/scss/forms/_validation.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../colors/index" as *;
3 | @use "../settings" as *;
4 |
5 | @if map.get($modules, "forms/validation") {
6 | // these are used so much below, and was making it too long for my liking.
7 | $excluded-elements: ':placeholder-shown, [type="button"], [type="reset"], [type="image"], [type="submit"], [type="checkbox"], [type="radio"]';
8 |
9 | #{$parent-selector} form:not([novalidate]) {
10 | input:user-valid:not(#{$excluded-elements}),
11 | input:user-invalid:not(#{$excluded-elements}) {
12 | padding-right: calc(1.5em + 0.75rem);
13 | background-position: right calc(0.375em + 0.1875rem) center;
14 | background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
15 | background-repeat: no-repeat;
16 | }
17 |
18 | select:user-valid:not([multiple], [size]),
19 | select:user-invalid:not([multiple], [size]) {
20 | padding-right: calc(1.5em + 0.75rem);
21 | padding-right: 4.2rem;
22 | background-position:
23 | right 0.75rem center,
24 | center right 2.25rem;
25 | background-repeat: no-repeat;
26 | }
27 |
28 | select:user-invalid:not([multiple], [size]) {
29 | background-image: var(#{$css-var-prefix}icon-chevron), var(#{$css-var-prefix}icon-invalid);
30 | }
31 |
32 | select:user-valid:not([multiple], [size]) {
33 | background-image: var(#{$css-var-prefix}icon-chevron), var(#{$css-var-prefix}icon-valid);
34 | }
35 |
36 | textarea:user-valid:not(:placeholder-shown),
37 | textarea:user-invalid:not(:placeholder-shown) {
38 | padding-right: calc(1.5em + 0.75rem);
39 | background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
40 | background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
41 | background-repeat: no-repeat;
42 | }
43 |
44 | input:user-invalid:not(#{$excluded-elements}),
45 | select:user-invalid:not([multiple], [size]),
46 | textarea:user-invalid:not(:placeholder-shown) {
47 | border-color: var(#{$css-var-prefix}form-element-invalid-border-color);
48 | background-image: var(#{$css-var-prefix}icon-invalid);
49 |
50 | &:focus {
51 | border-color: var(#{$css-var-prefix}form-element-invalid-active-border-color);
52 | }
53 | }
54 |
55 | input:user-valid:not(#{$excluded-elements}),
56 | select:user-valid:not([multiple], [size]),
57 | textarea:user-valid:not(:placeholder-shown) {
58 | border-color: var(#{$css-var-prefix}form-element-valid-border-color);
59 | background-image: var(#{$css-var-prefix}icon-valid);
60 |
61 | &:focus {
62 | border-color: var(#{$css-var-prefix}form-element-valid-active-border-color);
63 | }
64 | }
65 |
66 | input:required:user-invalid:is([type="checkbox"]) {
67 | border-color: var(#{$css-var-prefix}form-element-invalid-border-color);
68 | }
69 |
70 | input:user-valid:not(#{$excluded-elements}) + small[data-valid]::after,
71 | select:user-valid:not([multiple], [size]) + small[data-valid]::after,
72 | textarea:user-valid:not(:placeholder-shown) + small[data-valid]::after {
73 | content: attr(data-valid);
74 | color: var(#{$css-var-prefix}form-element-valid-border-color);
75 | }
76 |
77 | textarea:user-invalid:not(:placeholder-shown) + small[data-invalid]::after,
78 | select:user-invalid:not([multiple], [size]) + small[data-invalid]::after,
79 | input:user-invalid:not(#{$excluded-elements}) + [data-invalid]::after,
80 | input[type="file"]:user-invalid + ul + [data-invalid]::after {
81 | content: attr(data-invalid);
82 | color: var(#{$css-var-prefix}form-element-invalid-border-color);
83 | }
84 |
85 | input:user-valid:not(#{$excluded-elements}) + [data-valid]::after,
86 | input:user-invalid:not(#{$excluded-elements}) + [data-invalid]::after,
87 | textarea:user-valid:not(:placeholder-shown) + [data-valid]::after,
88 | input[type="file"]:user-invalid + [data-invalid]::after,
89 | input[type="file"]:user-invalid + ul + [data-invalid]::after {
90 | display: block;
91 | }
92 |
93 | input[type="file"]:user-invalid + ul + [data-invalid]::after {
94 | position: relative;
95 | top: -2rem;
96 | }
97 |
98 | // the file btn
99 | input[type="file"]:user-invalid::file-selector-button {
100 | border-color: var(#{$css-var-prefix}form-element-invalid-border-color);
101 | background-color: var(#{$css-var-prefix}form-element-invalid-border-color);
102 | }
103 |
104 | /* File list when selected from the file input */
105 | .file-list {
106 | padding-left: 0;
107 | li {
108 | display: flex;
109 | align-items: center;
110 | justify-content: space-between;
111 | list-style-type: none;
112 | text-align: center;
113 |
114 | &:nth-child(2n) {
115 | background-color: $grey-900;
116 | }
117 |
118 | &:hover {
119 | background-color: var(#{$css-var-prefix}muted-border-color);
120 | }
121 |
122 | .btn-file-rm {
123 | display: inline-block;
124 | width: 1rem;
125 | height: 1rem;
126 | margin-right: 10px;
127 | padding: 0;
128 | padding-top: calc(var(#{$css-var-prefix}spacing) * 0.25);
129 | border: none;
130 | background-image: var(#{$css-var-prefix}icon-red-close);
131 | background-position: center;
132 | background-size: 1rem;
133 | background-repeat: no-repeat;
134 | background-color: transparent;
135 | color: var(#{$css-var-prefix}element-invalid-border-color);
136 | opacity: 0.5;
137 | transition: opacity var(#{$css-var-prefix}transition);
138 |
139 | &:hover {
140 | opacity: 1;
141 | }
142 | }
143 | }
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/scss/helpers/_copyright.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Pico CSS ✨ v2.2.10 (https://github.com/Yohn/PicoCSS)
3 | * Copyright 2019-2025 - Licensed under MIT
4 | */
5 |
--------------------------------------------------------------------------------
/scss/helpers/_functions.scss:
--------------------------------------------------------------------------------
1 | @use "sass:color";
2 | @use "sass:string";
3 |
4 | // Display color as RGB
5 | @function display-rgb($color) {
6 | @return string.unquote(
7 | "rgb(" + color.channel($color, "red", $space: rgb) + ", " +
8 | color.channel($color, "green", $space: rgb) + ", " +
9 | color.channel($color, "blue", $space: rgb) + ")"
10 | );
11 | }
12 |
13 | // Generate a shadow layer
14 | @function shadow-layer($elevation, $blur, $opacity, $color) {
15 | @return #{($elevation * 0.5)} #{$elevation} #{$blur} #{rgba($color, $opacity)};
16 | }
17 |
18 | // Generate a shadow with 7 layers
19 | @function shadow($color) {
20 | $box-shadow-elevation: 1rem;
21 | $box-shadow-blur-strength: 6rem;
22 | $box-shadow-opacity: 0.06;
23 |
24 | @return shadow-layer(
25 | $box-shadow-elevation * 0.029,
26 | $box-shadow-blur-strength * 0.029,
27 | $box-shadow-opacity * 0.283,
28 | $color
29 | ),
30 | shadow-layer(
31 | $box-shadow-elevation * 0.067,
32 | $box-shadow-blur-strength * 0.067,
33 | $box-shadow-opacity * 0.4,
34 | $color
35 | ),
36 | shadow-layer(
37 | $box-shadow-elevation * 0.125,
38 | $box-shadow-blur-strength * 0.125,
39 | $box-shadow-opacity * 0.5,
40 | $color
41 | ),
42 | shadow-layer(
43 | $box-shadow-elevation * 0.225,
44 | $box-shadow-blur-strength * 0.225,
45 | $box-shadow-opacity * 0.6,
46 | $color
47 | ),
48 | shadow-layer(
49 | $box-shadow-elevation * 0.417,
50 | $box-shadow-blur-strength * 0.417,
51 | $box-shadow-opacity * 0.717,
52 | $color
53 | ),
54 | shadow-layer($box-shadow-elevation, $box-shadow-blur-strength, $box-shadow-opacity, $color),
55 | 0 0 0 0.0625rem #{rgba($color, ($box-shadow-opacity * 0.25))};
56 | }
57 |
58 | // tint-color, shade-color, and shift-color
59 | // borrowed from Bootstrap 5.3.3
60 | // MIT License github.com/twbs/bootstrap
61 | // Tint a color: mix a color with white
62 | @function tint-color($color, $weight) {
63 | @return mix(white, $color, $weight);
64 | }
65 |
66 | // Shade a color: mix a color with black
67 | @function shade-color($color, $weight) {
68 | @return mix(black, $color, $weight);
69 | }
70 |
71 | // Shade the color if the weight is positive, else tint it
72 | @function shift-color($color, $weight) {
73 | @return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight));
74 | }
75 |
--------------------------------------------------------------------------------
/scss/layout/_container.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/container") and $enable-classes {
5 | /**
6 | * Container
7 | */
8 |
9 | #{$parent-selector} .container,
10 | #{$parent-selector} .container-fluid {
11 | width: 100%;
12 | margin-inline: auto;
13 | padding-inline: var(#{$css-var-prefix}block-spacing-horizontal);
14 | }
15 |
16 | #{$parent-selector} .container {
17 | $first-breakpoint: true;
18 | @each $key, $values in $breakpoints {
19 | @if $values {
20 | @media (min-width: map.get($values, "breakpoint")) {
21 | max-width: map.get($values, "viewport");
22 | @if $first-breakpoint {
23 | $first-breakpoint: false;
24 | padding-inline: 0;
25 | }
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/scss/layout/_document.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/document") {
5 | /**
6 | * Document
7 | * Content-box & Responsive typography
8 | */
9 |
10 | // Reboot based on :
11 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
12 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
13 | // ––––––––––––––––––––
14 |
15 | // 1. Add border box sizing in all browsers (opinionated)
16 | // 2. Backgrounds do not repeat by default (opinionated)
17 | *,
18 | *::before,
19 | *::after {
20 | box-sizing: border-box; // 1
21 | background-repeat: no-repeat; // 2
22 | }
23 |
24 | // 1. Add text decoration inheritance in all browsers (opinionated)
25 | // 2. Add vertical alignment inheritance in all browsers (opinionated)
26 | ::before,
27 | ::after {
28 | text-decoration: inherit; // 1
29 | vertical-align: inherit; // 2
30 | }
31 |
32 | // 1. Change the line height in all browsers (opinionated)
33 | // 2. Breaks words to prevent overflow in all browsers (opinionated)
34 | // 3. Use a 4-space tab width in all browsers (opinionated)
35 | // 4. Remove the grey highlight on links in iOS (opinionated)
36 | // 5. Prevent adjustments of font size after orientation changes in iOS
37 | :where(:root),
38 | :where(:host) {
39 | -webkit-tap-highlight-color: transparent; // 4
40 | -webkit-text-size-adjust: 100%; // 5
41 | text-size-adjust: 100%; // 5
42 | background-color: var(#{$css-var-prefix}background-color);
43 | color: var(#{$css-var-prefix}color);
44 | font-weight: var(#{$css-var-prefix}font-weight);
45 | font-size: var(#{$css-var-prefix}font-size);
46 | line-height: var(#{$css-var-prefix}line-height); // 1
47 | font-family: var(#{$css-var-prefix}font-family);
48 | text-underline-offset: var(#{$css-var-prefix}text-underline-offset);
49 | text-rendering: optimizeLegibility;
50 | overflow-wrap: break-word; // 2
51 | tab-size: 2; // 3
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/scss/layout/_grid.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/grid") and $enable-classes {
5 | /**
6 | * Grid
7 | * Minimal grid system with auto-layout columns
8 | */
9 |
10 | #{$parent-selector} .grid {
11 | grid-column-gap: var(#{$css-var-prefix}grid-column-gap);
12 | grid-row-gap: var(#{$css-var-prefix}grid-row-gap);
13 | display: grid;
14 | grid-template-columns: 1fr;
15 |
16 | @if map.get($breakpoints, "md") {
17 | @media (min-width: map.get(map.get($breakpoints, "md"), "breakpoint")) {
18 | grid-template-columns: repeat(auto-fit, minmax(0%, 1fr));
19 | }
20 | }
21 |
22 | & > * {
23 | min-width: 0; // HACK for children in overflow
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/scss/layout/_landmarks.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/landmarks") {
5 | /**
6 | * Landmarks
7 | */
8 |
9 | // Reboot based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // add smooth scrolling
15 | html {
16 | scroll-behavior: smooth;
17 | }
18 |
19 | // 1. Remove the margin in all browsers (opinionated)
20 | body {
21 | width: 100%;
22 | margin: 0; // 1
23 | }
24 |
25 | // Render the `main` element consistently in IE
26 | main {
27 | display: block;
28 | }
29 |
30 | // Pico
31 | // ––––––––––––––––––––
32 | #{$parent-selector} #{$semantic-root-element} {
33 | > header,
34 | > main,
35 | > footer {
36 | // , , as containers
37 | @if $enable-semantic-container {
38 | $first-breakpoint: true;
39 | width: 100%;
40 | margin-right: auto;
41 | margin-left: auto;
42 | padding: var(#{$css-var-prefix}block-spacing-vertical)
43 | var(#{$css-var-prefix}block-spacing-horizontal);
44 |
45 | @if $enable-viewport {
46 | @each $key, $values in $breakpoints {
47 | @if $values {
48 | @media (min-width: map.get($values, "breakpoint")) {
49 | max-width: map.get($values, "viewport");
50 | @if $first-breakpoint {
51 | $first-breakpoint: false;
52 | padding-right: 0;
53 | padding-left: 0;
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | // Regular vertical spacings for , ,
62 | @else {
63 | padding-block: var(#{$css-var-prefix}block-spacing-vertical);
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/scss/layout/_overflow-auto.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/overflow-auto") and $enable-classes {
5 | /**
6 | * Overflow auto
7 | */
8 |
9 | #{$parent-selector} .overflow-auto {
10 | overflow: auto;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/scss/layout/_row.scss:
--------------------------------------------------------------------------------
1 | @use "sass:string";
2 | @use "sass:map";
3 | @use "sass:math";
4 | @use "../settings" as *; // for spacing, breakpoints, and if columns are defined.
5 |
6 | /* Source inspired by https://github.com/sophie-thomas/CSS-Grid/blob/main/assets/scss/grid.scss */
7 |
8 | @if map.get($modules, "layout/row") and $enable-classes {
9 | $helper-cols: "";
10 | $helper-offset: "";
11 | /*--- CSS Grid ---*/
12 | #{$parent-selector} .row-fluid,
13 | #{$parent-selector} .row {
14 | display: grid;
15 | grid-template-columns: repeat($row-columns, 1fr);
16 | gap: var(#{$css-var-prefix}grid-row-gap) var(#{$css-var-prefix}grid-column-gap);
17 | &.align-center {
18 | align-items: center;
19 | }
20 | &.align-start {
21 | align-items: start;
22 | }
23 | &.align-end {
24 | align-items: end;
25 | }
26 | > [class*="col"] > *,
27 | > [class|="col"] > *,
28 | > [class~="col"] > * {
29 | margin: var(#{$css-var-prefix}block-spacing-vertical) auto;
30 | }
31 | }
32 | #{$parent-selector} .row {
33 | max-width: map.get(map.get($breakpoints, "xl"), "viewport");
34 | margin: 0 auto;
35 | }
36 | /* Defining columns spans and offsets */
37 | // Loop through all column spans and define the .grid-column-end number
38 | @for $col from 1 through $row-columns {
39 | #{$parent-selector} .col-#{$col} {
40 | grid-column-end: span $col;
41 | }
42 | @if $col == 1 {
43 | $helper-cols: ".col-1";
44 | } @else {
45 | $helper-cols: #{$helper-cols} + ", #{$parent-selector} .col-" + #{$col};
46 | }
47 | }
48 |
49 | // Loop through all column offsets and define the .grid-column-start number
50 | @for $offset from 0 through $row-columns - 1 {
51 | #{$parent-selector} .offset-#{$offset} {
52 | grid-column-start: $offset + 1;
53 | }
54 | @if $offset == 0 {
55 | $helper-offset: ".offset-0";
56 | } @else {
57 | $helper-offset: #{$helper-offset} + ", .offset-" + #{$offset};
58 | }
59 | }
60 | // Defines media queries for each breakpoint and loops through both spans
61 | // and offsets to set grid-column-end and grid-column-start
62 | // Loop through breakpoints and generate media queries
63 |
64 | @each $breakpoint, $values in $breakpoints {
65 | @if $values {
66 | @media (min-width: map.get($values, "viewport")) {
67 | @for $col from 1 through $row-columns {
68 | #{$parent-selector} .col-#{$breakpoint}-#{$col} {
69 | grid-column-end: span $col;
70 | }
71 | @if ($breakpoint != "sm") {
72 | $helper-cols: #{$helper-cols} +
73 | ", #{$parent-selector} .col-" +
74 | #{$breakpoint} +
75 | "-" +
76 | #{$col};
77 | }
78 | }
79 | @for $offset from 0 through $row-columns - 1 {
80 | #{$parent-selector} .offset-#{$breakpoint}-#{$offset} {
81 | grid-column-start: $offset + 1;
82 | }
83 | @if ($breakpoint != "sm") {
84 | $helper-offset: #{$helper-offset} + ", .offset-" + #{$breakpoint} + "-" + #{$offset};
85 | }
86 | }
87 | }
88 | }
89 | }
90 |
91 | /* CSS Grid Media Queries */
92 | // Sets the columns to be col-12 with a starting column of 1
93 | $sm-size: map.get(map.get($breakpoints, "sm"), "viewport");
94 |
95 | @media (max-width: $sm-size) {
96 | //[class*="col-"] {
97 | #{$helper-cols} {
98 | grid-column-end: span $row-columns;
99 | }
100 | //[class*="offset-"] {
101 | #{$helper-offset} {
102 | grid-column-start: 1;
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/scss/layout/_section.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "layout/section") {
5 | /**
6 | * Section
7 | */
8 |
9 | #{$parent-selector} section {
10 | margin-bottom: var(#{$css-var-prefix}block-spacing-vertical);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/scss/pico.classless.scss:
--------------------------------------------------------------------------------
1 | // Pico classless version
2 | @use "index" with (
3 | $enable-semantic-container: true,
4 | $enable-classes: false
5 | );
6 |
--------------------------------------------------------------------------------
/scss/pico.colors.scss:
--------------------------------------------------------------------------------
1 | @use "helpers/copyright";
2 | @use "colors/utilities";
3 |
--------------------------------------------------------------------------------
/scss/pico.conditional.scss:
--------------------------------------------------------------------------------
1 | // Pico conditional version
2 | @use "index" with (
3 | $parent-selector: ".pico"
4 | );
5 |
--------------------------------------------------------------------------------
/scss/pico.fluid.classless.conditional.scss:
--------------------------------------------------------------------------------
1 | // Pico fluid classless conditional version
2 | @use "../scss" with (
3 | $enable-semantic-container: true,
4 | $enable-viewport: false,
5 | $enable-classes: false,
6 | $parent-selector: ".pico"
7 | );
8 |
--------------------------------------------------------------------------------
/scss/pico.fluid.classless.scss:
--------------------------------------------------------------------------------
1 | // Pico fluid classless version
2 | @use "index" with (
3 | $enable-semantic-container: true,
4 | $enable-viewport: false,
5 | $enable-classes: false
6 | );
7 |
--------------------------------------------------------------------------------
/scss/pico.scss:
--------------------------------------------------------------------------------
1 | @forward "settings";
2 | @use "index";
3 |
--------------------------------------------------------------------------------
/scss/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | syntax: "postcss-scss",
3 | map: false,
4 | plugins: {
5 | "css-declaration-sorter": {
6 | order: "smacss"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/scss/themes/_default.scss:
--------------------------------------------------------------------------------
1 | // Styles
2 | @use "default/styles";
3 |
4 | // Colors schemes
5 | @use "default/schemes";
6 |
--------------------------------------------------------------------------------
/scss/themes/default/_schemes.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../../settings" as *;
3 |
4 | @use "light";
5 | @use "dark";
6 |
7 | @if map.get($modules, "themes/default") {
8 | /**
9 | * Color schemes
10 | */
11 |
12 | // Light color scheme (Default)
13 | // Can be forced with data-theme="light"
14 | [data-theme="light"],
15 | :root:not([data-theme="dark"]),
16 | :host(:not([data-theme="dark"])) {
17 | @include light.theme;
18 | }
19 |
20 | // Dark color scheme (Auto)
21 | // Automatically enabled if user has Dark mode enabled
22 | @media only screen and (prefers-color-scheme: dark) {
23 | :root:not([data-theme]),
24 | :host(:not([data-theme])) {
25 | @include dark.theme;
26 | }
27 | }
28 |
29 | // Dark color scheme (Forced)
30 | // Enabled if forced with data-theme="dark"
31 | [data-theme="dark"] {
32 | @include dark.theme;
33 | }
34 |
35 | #{$parent-selector} progress,
36 | #{$parent-selector} [type="checkbox"],
37 | #{$parent-selector} [type="radio"],
38 | #{$parent-selector} [type="range"] {
39 | accent-color: var(#{$css-var-prefix}primary);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/scss/utilities/_accessibility.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "utilities/accessibility") {
5 | /**
6 | * Accessibility & User interaction
7 | */
8 |
9 | // Based on :
10 | // - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
11 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
12 | // ––––––––––––––––––––
13 |
14 | // Accessibility
15 |
16 | // Change the cursor on control elements in all browsers (opinionated)
17 | #{$parent-selector} [aria-controls] {
18 | cursor: pointer;
19 | }
20 |
21 | // Change the cursor on disabled, not-editable, or otherwise inoperable elements in all browsers (opinionated)
22 | #{$parent-selector} [aria-disabled="true"],
23 | #{$parent-selector} [disabled] {
24 | cursor: not-allowed;
25 | }
26 |
27 | // Change the display on visually hidden accessible elements in all browsers (opinionated)
28 | #{$parent-selector} [aria-hidden="false"][hidden] {
29 | display: initial;
30 | }
31 |
32 | #{$parent-selector} [aria-hidden="false"][hidden]:not(:focus) {
33 | clip: rect(0, 0, 0, 0);
34 | position: absolute;
35 | }
36 |
37 | // User interaction
38 | // Remove the tapping delay in IE 10
39 | #{$parent-selector} a,
40 | #{$parent-selector} area,
41 | #{$parent-selector} button,
42 | #{$parent-selector} input,
43 | #{$parent-selector} label,
44 | #{$parent-selector} select,
45 | #{$parent-selector} summary,
46 | #{$parent-selector} textarea,
47 | #{$parent-selector} [tabindex] {
48 | -ms-touch-action: manipulation;
49 | }
50 |
51 | // Pico
52 | // ––––––––––––––––––––
53 |
54 | #{$parent-selector} [dir="rtl"] {
55 | direction: rtl;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/scss/utilities/_reduce-motion.scss:
--------------------------------------------------------------------------------
1 | @use "sass:map";
2 | @use "../settings" as *;
3 |
4 | @if map.get($modules, "utilities/reduce-motion") and enable-transitions and enable-important {
5 | /**
6 | * Reduce Motion Features
7 | */
8 |
9 | // Based on :
10 | // - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css
11 | // ––––––––––––––––––––
12 |
13 | // 1. Remove animations when motion is reduced (opinionated)
14 | // 2. Remove fixed background attachments when motion is reduced (opinionated)
15 | // 3. Remove timed scrolling behaviors when motion is reduced (opinionated)
16 | // 4. Remove transitions when motion is reduced (opinionated)
17 | @media (prefers-reduced-motion: reduce) {
18 | #{$parent-selector} *:not([aria-busy="true"]),
19 | #{$parent-selector} :not([aria-busy="true"])::before,
20 | #{$parent-selector} :not([aria-busy="true"])::after {
21 | background-attachment: initial !important; // 2
22 | animation-duration: 1ms !important; // 1
23 | animation-delay: -1ms !important; // 1
24 | animation-iteration-count: 1 !important; // 1
25 | scroll-behavior: auto !important; // 3
26 | transition-delay: 0s !important; // 4
27 | transition-duration: 0s !important; // 4
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------