├── .circleci
└── config.yml
├── .github
└── FUNDING.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example
└── reagent-demo
│ ├── .gitignore
│ ├── README.md
│ ├── deps.edn
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── shadow-cljs.edn
│ └── src
│ └── acme
│ └── frontend
│ ├── annotated.cljc
│ ├── app.cljc
│ ├── comprehensive.cljc
│ ├── my_css.cljc
│ └── my_grammar.cljc
└── lib
├── girouette
├── .gitignore
├── bin
│ └── kaocha
├── deps.edn
├── package-lock.json
├── package.json
├── pom.xml
├── src
│ └── girouette
│ │ ├── core.cljc
│ │ ├── garden
│ │ └── util.cljc
│ │ ├── grammar
│ │ └── hiccup_tag.cljc
│ │ ├── tw
│ │ ├── accessibility.cljc
│ │ ├── animation.cljc
│ │ ├── background.cljc
│ │ ├── border.cljc
│ │ ├── box_alignment.cljc
│ │ ├── color.cljc
│ │ ├── common.cljc
│ │ ├── core.cljc
│ │ ├── default_api.cljc
│ │ ├── effect.cljc
│ │ ├── filter.cljc
│ │ ├── flexbox.cljc
│ │ ├── grid.cljc
│ │ ├── interactivity.cljc
│ │ ├── layout.cljc
│ │ ├── preflight.cljc
│ │ ├── sizing.cljc
│ │ ├── spacing.cljc
│ │ ├── svg.cljc
│ │ ├── table.cljc
│ │ ├── transform.cljc
│ │ └── typography.cljc
│ │ ├── util.cljc
│ │ └── version.cljc
├── test
│ └── girouette
│ │ ├── garden
│ │ └── util_test.cljc
│ │ ├── grammar
│ │ └── hiccup_tag_test.cljc
│ │ ├── tw
│ │ ├── accessibility_test.cljc
│ │ ├── animation_test.cljc
│ │ ├── background_test.cljc
│ │ ├── border_test.cljc
│ │ ├── box_alignment_test.cljc
│ │ ├── color_test.cljc
│ │ ├── common_test.cljc
│ │ ├── core_test.cljc
│ │ ├── effect_test.cljc
│ │ ├── filter_test.cljc
│ │ ├── flexbox_test.cljc
│ │ ├── grid_test.cljc
│ │ ├── interactivity_test.cljc
│ │ ├── layout_test.cljc
│ │ ├── sizing_test.cljc
│ │ ├── spacing_test.cljc
│ │ ├── svg_test.cljc
│ │ ├── table_test.cljc
│ │ ├── transform_test.cljc
│ │ └── typography_test.cljc
│ │ └── version_test.cljc
└── tests.edn
└── processor
├── .gitignore
├── deps.edn
├── pom.xml
└── src
└── girouette
├── processor.clj
└── processor
└── env.clj
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/clojure:tools-deps-1.10.1.763-node
6 | working_directory: ~/girouette/lib/girouette
7 | steps:
8 | - checkout:
9 | path: ~/girouette
10 | - restore_cache:
11 | keys:
12 | - 'clj-v1-{{ checksum "deps.edn" }}-{{ checksum "package-lock.json" }}'
13 | - 'clj-v1'
14 | - run: npm ci
15 | - run: mkdir -p test-results
16 | - run: bin/kaocha --plugin kaocha.plugin/junit-xml --junit-xml-file test-results/kaocha/results.xml
17 | - store_test_results:
18 | path: test-results
19 | - save_cache:
20 | key: 'clj-v1-{{checksum "deps.edn"}}-{{ checksum "package-lock.json" }}'
21 | paths:
22 | - ~/.m2
23 | - ~/.cljs/.aot_cache
24 | - ~/node_modules
25 | - ~/.gitlibs
26 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # 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: vincentcantin # 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 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Unreleased
2 |
3 | ## Fixed
4 |
5 | - "Text decoration" now uses the property "text-decoration-line" instead of "text-decoration".
6 |
7 | ## Added
8 |
9 | - Class "overline".
10 |
11 | ## v0.0.10
12 |
13 | ## Fixed
14 |
15 | - (PR #93) Fixed an inversion left-right in the `divide-x-..` classes. Thanks to `andrei-zhidkov` for the fix.
16 | - (PR #94) Fixes the format of the CSS colors in `box-shadow`. Thanks to `zolazhou` for the fix.
17 |
18 | ## v0.0.9
19 |
20 | ## Fixed
21 |
22 | - (PR #92) Fixed the CSS Selectors broken in `v0.0.8`. Thanks to `zolazhou`.
23 |
24 | ## v0.0.8
25 |
26 | ## Added
27 |
28 | - (PR #83) Compatibility with Tailwind `v3.0.23`, except for the Tailwind-styled "arbitrary values" specified between `[` and `]`.
29 | Thanks to `jamesnvc` for this large contribution.
30 | - Added Tailwind v3's "colors" and "extended colors".
31 | - Added fields `:since-version` and `:removed-in-version` on Girouette components.
32 | - Added a function to filter components based on a version value. Users can
33 | select components for the version they want, e.g. `[:tw 2]` or `[:tw 3]`,
34 | making Girouette (hope)fully backward compatible.
35 | - (Issue #91) Added a more recent v3.0.24 version of Preflight (v3.0.24), selectable via the settings of the Girouette processor.
36 |
37 | ## Fixed
38 |
39 | - (PR #85) Implemented `--gi-divide-*-reverse`.
40 | Thanks to `flyingmachine` for the bug report and fix.
41 | - Cljdoc is analyzing Girouette correctly again. Thanks to `ribelo` and `lread` for their help.
42 |
43 | ## Changed
44 |
45 | - (PR #86) Swap hawk for beholder. It results in a faster response of the CSS processor tool while in the watch mode.
46 | Thanks to `dpassen` for the contribution.
47 | - Updated the example project to use the Girouette API setup compatible with Tailwind v3.
48 |
49 | ## Breaking changes
50 |
51 | Some symbols have been renamed to better reflect the multiple versions supported by Girouette:
52 | - `girouette.tw.color/default-color-map` -> `girouette.tw.color/tw-v2-colors`
53 | - `girouette.tw.default-api/default-components` -> `girouette.tw.default-api/all-tw-components`
54 | - `girouette.tw.default-api/class-name->garden` -> `girouette.tw.default-api/tw-v2-class-name->garden`
55 | - `girouette.tw.typography/default-font-family-map` -> `girouette.tw.typography/tw-v2-font-family-map`
56 | - `girouette.tw.preflight/preflight` -> `girouette.tw.preflight/preflight-v2_0_3`
57 |
58 | The processor's params have changed (see the [example project](https://github.com/green-coder/girouette/blob/fd0f7cbb017ea5a989c5ce01149c67896aaca977/example/reagent-demo/deps.edn)):
59 | - `garden-fn` is no longer optional, you need to provide a qualified symbol.
60 | - `preflight?` was replaced by `base-css-rules`, it takes a vector of qualified symbols.
61 |
62 | ## v0.0.7
63 |
64 | ### Fixed
65 |
66 | Thanks to `jamesnvc` for the following fixes:
67 | - (PR #76) Escape octothorpe character in class name.
68 | - (PR #77) Fix `.invisible` class.
69 | - (PR #80) Fix the rule `:max-width`.
70 | - Fix bug in the processor tool when parsing CLJS files containing character literals.
71 |
72 | - Fixed and simplified the function `girouette.util/rule-comparator`.
73 |
74 | ## v0.0.6
75 |
76 | ### Changed
77 |
78 | - (PR #74) Changed the garden data output for components like `space-x-2` to make it easier to be processed by libraries like Ornament.
79 | The change won't affect most end users as the new garden data has the same effect, CSS-wise.
80 | Big thank to `Vynlar` for this cross-project contribution.
81 |
82 | ## v0.0.5
83 |
84 | ### Added
85 |
86 | - `girouette.garden.util/rule-comparator` can be used for ordering the garden rules which are output by `class-name->garden`.
87 | Related to issue #71 and PR #66.
88 |
89 | ### Fixed
90 |
91 | - (issue #72) Added missing cases for the `max-width` rule. Thanks to `joe-loco` for the bug report.
92 | - (issue #71) The media queries are now coming after the non-media queries in the style file. Thanks to `joe-loco` for the bug report.
93 | - (PR #73) Fixed the line height on text-5xl ~ text-9xl. Thanks to `jeremS` for the PR.
94 |
95 | ## v0.0.4
96 |
97 | ### Added
98 |
99 | - SCSS's @apply equivalent (issue #35 and PR #64)
100 | - Partial ordering between different rules (commit e0e9ab34d2bdefb7b7289ab15e06a3e243dc230c)
101 | - The return value of `class-name->garden` now has some metadata:
102 | - It's useful during the development of the Girouette components.
103 | - The metadata also contains the information about how the garden rules should be ordered in a CSS file.
104 | - Added `:dry-run?` flag in the inputs of the processor.
105 |
106 | ### Fixed
107 |
108 | - Fixed the state variant "first", "last", "odd" and "even" (PR #62)
109 | - Fixed a typo in Preflight (PR #63)
110 | - Fixed typo in :font-variant-numeric rule (PR #68)
111 | - Fixed typo in :divide-width rule
112 | - Fixed minor bugs in the processor (issues #60 and #61)
113 |
114 | ## v0.0.3
115 |
116 | ### Added
117 |
118 | - Added support for HSL colors.
119 | - Made the colors easy to customize. Updated the demo.
120 | - Made the font-family easy to customize.
121 | - Extended the rule `:line-height` to work with any number.
122 | - `make-api` does no longer need to have specified colors or font families to work.
123 |
124 | ### Fixed
125 |
126 | - (issue #55) The "display" rule is now working correctly when used with a variant like "sm:".
127 | Thanks to @jacobobryant for reporting it.
128 |
129 | ## v0.0.2
130 |
131 | ### Added
132 |
133 | - Preflight CSS from Tailwind was converted into Garden, to be used as a base by the processor tool.
134 | - "font-size-..." Girouette component.
135 | - "line-height-..." Girouette component.
136 |
137 | ### Changed
138 |
139 | - Source code clean up for Cljdoc.
140 |
141 | ### Fixed
142 |
143 | - "min-width" and "max-width" were not implemented correctly.
144 | - "font-weight" was not implemented correctly.
145 |
146 | ## v0.0.1
147 |
148 | Initial release
149 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Vincent Cantin and contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to use,
6 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Girouette [](https://circleci.com/gh/green-coder/girouette)
2 |
3 | [](https://clojars.org/girouette)
4 | [](https://cljdoc.org/d/girouette/girouette/CURRENT)
5 | [](https://clojurians.slack.com/archives/C01J8H2VD97)
6 | [](https://clojars.org/girouette)
7 |
8 |
9 | [](https://commons.wikimedia.org/wiki/File:Girouette_Bateau_Yeu.jpg)
10 |
11 | > Dès que le vent soufflera, je repartira.
12 | > Dès que les vents tourneront, nous nous en allerons.
13 |
14 | Girouette is a grammar-based, generative approach to CSS.
15 | It translates a classname into a Garden data structure representing CSS.
16 |
17 | ```clojure
18 | (class-name->garden "w-10")
19 | ; => [".w-10" {:width "2.5rem"}]
20 | ```
21 |
22 | Girouette also makes it easy to use your own grammar rules to generate anything
23 | you may dream of.
24 |
25 | ## Introduction
26 |
27 | CSS libraries like Tailwind and Tachyons provide quantities of predefined class names,
28 | hoping to cover most of the needs of their users. But because of combinatory explosion,
29 | they cannot provide all the class names users will ever need, in which case the users will
30 | have to hack their way with config files and/or handwritten CSS.
31 | When releasing for production, the unused CSS classes are removed from the CSS files using
32 | tools like PurgeCSS.
33 |
34 | Girouette is taking the opposite approach where we first look at which class names are used,
35 | and from their names and a grammar associated with some generation rules, the corresponding
36 | CSS content is created.
37 |
38 | This "on demand" generative approach allows to have any combination of parameters in a CSS
39 | class name, while opening the door to the most creative grammars which a human would want
40 | to use to communicate its intent.
41 |
42 | (*UPDATE:* a couple of months after Girouette was released, the author of Tailwind implemented
43 | in its version 2 and 3 an "on demand" feature very similar to Girouette)
44 |
45 | ### Documentation & Resources
46 |
47 | Girouette currently has components which makes it compatible (with a few caveats) with:
48 | - [Tailwind v2.0.2](https://v2.tailwindcss.com/docs)
49 | - [Tailwind v3.0.23](https://tailwindcss.com/docs)
50 |
51 | Presentation @ the Bay Area Clojure Meetup:
52 | - [The slides](https://app.pitch.com/app/presentation/a760be33-4a5b-4e73-bd25-07387cd197dc/7282e9fa-8789-43bc-8b2d-eaec38711d98)
53 | - [Video recording](https://www.youtube.com/watch?v=Tnv6SvZM6tg)
54 |
55 | The project has example projects in `example/`:
56 | - A simple [demo project using Reagent](example/reagent-demo).
57 |
58 | ## How it works
59 |
60 | `Girouette` is using the awesome [`Instaparse`](https://github.com/Engelberg/instaparse)
61 | library for parsing the class names, and is converting them into the
62 | [`Garden`](https://github.com/noprompt/garden) format.
63 |
64 | Its API mainly consists in the function `class-name->garden` which is pretty explicit.
65 |
66 | ```clojure
67 | (class-name->garden "w-42%")
68 | ;=> [".w-42\\%" {:width "42%"}]
69 | ```
70 |
71 | You can use `Girouette processor tool` to extract the CSS class names from
72 | your source code and generate the CSS in real time as you develop.
73 |
74 | See the [demo project](example/reagent-demo) for more information.
75 |
76 |
77 | ## Advantages of this approach
78 |
79 | With the right Girouette components in place, any parameters can be combined
80 | in class names without leaving your usual REPL workflow.
81 |
82 | ### Large range on numbers
83 |
84 | No need to stop what you are doing and to modify some config files just because
85 | `mx-13` is not supported by default while `mx-12` is.
86 |
87 | Any color can be represented directly in class names,
88 | like `rgba-f59d` or `rgba-ff5599dd`.
89 |
90 | ### Limitless class name descriptiveness
91 |
92 | It is possible to create grammars which support very long class names.
93 |
94 | ```clojure
95 | ;; Example of class name:
96 | "bg-gradient-to-right-red-orange-yellow-green-blue-purple"
97 |
98 | ;; Instaparse rule:
99 | "bg-gradient = <'bg-gradient-to-'> gradient-direction (<'-'> color)+
100 | gradient-direction = 'top' | 'right' | 'bottom' | 'left' | angle
101 | color = ...
102 | "
103 | ```
104 |
105 | ## Link to other CSS projects
106 |
107 | ### In Clojure
108 |
109 | A few other alternatives are available.
110 |
111 | - [Tailwind Garden](https://github.com/wilkerlucio/tailwind-garden)
112 | - [macrocss](https://github.com/HealthSamurai/macrocss)
113 | - [tailwind-hiccup](https://github.com/rgm/tailwind-hiccup)
114 | - [tailwind-clj](https://github.com/mrmcc3/tailwind-clj)
115 |
116 | ### In JS
117 |
118 | ### Atomizer
119 |
120 | [Atomizer](https://acss.io/) is an older project which is also interpreting CSS class names.
121 |
122 | ### WindiCSS
123 |
124 | Independently and in parallel of Girouette's development, [WindiCSS](https://github.com/windicss/windicss)
125 | was developed with similar ideas. Please check it out, specially if you are developing directly in the JS environment.
126 |
127 | ## Who is using Girouette
128 |
129 | - The [Ornament](https://github.com/lambdaisland/ornament) library, by [Lambda Island / Gaiwan](https://gaiwan.co/):
130 | A very elegant way to craft and integrate CSS rules inside your Clojure(script) apps.
131 |
132 | (To add your project to this list, just edit this file and open a pull request)
133 |
134 | ## Contribute
135 |
136 | Contributions are very welcome, just make sure that the contributions are your own,
137 | and add proper credits in the commit messages if it is not the case.
138 |
139 | ## License terms
140 |
141 | This project is distributed under the `MIT License`, which is available at
142 | https://opensource.org/licenses/MIT
143 |
144 | Copyright (c) Vincent Cantin and contributors.
145 |
--------------------------------------------------------------------------------
/example/reagent-demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | public/js
3 | public/style/girouette.css
4 |
5 | /target
6 | /checkouts
7 | /src/gen
8 |
9 | pom.xml
10 | pom.xml.asc
11 | *.iml
12 | *.jar
13 | *.log
14 | .shadow-cljs
15 | .clj-kondo
16 | .idea
17 | .nrepl-*
18 | .DS_Store
19 | .cpcache
20 |
21 | *~
22 | [#]*[#]
23 | .\#*
24 |
--------------------------------------------------------------------------------
/example/reagent-demo/README.md:
--------------------------------------------------------------------------------
1 | # Reagent demo
2 |
3 | This demonstrates one of the way Girouette can be used in a
4 | Single Page Application (SPA) using Reagent.
5 |
6 | The same process should work with many other front end frameworks.
7 |
8 | ## How it works
9 |
10 | We use the macro `girouette.core/css` to annotate the CSS class names in the source code
11 | and differentiate them from any other text the source code might contain.
12 |
13 | The Girouette Processor tool is then parsing the source code in the background, finds the
14 | CSS class names, and generates the content of `public/style/girouette.css` based on
15 | the CSS class names found.
16 |
17 | ## Launching the webapp
18 |
19 | Load modules used by Shadow-CLJS (you only need to do that once):
20 | ```shell
21 | npm i
22 | ```
23 |
24 | Then launch the compiler in watch mode:
25 | ```shell
26 | shadow-cljs watch frontend
27 | ```
28 |
29 | In parallel, launch Girouette's CSS processor in watch mode:
30 | ```shell
31 | clojure -X:girouette-processor
32 | ```
33 |
34 | Browse your webapp by clicking on the link displayed by the compiler
35 | after completion of the compilation.
36 |
37 | At that point, the front end Clojurescript code and the CSS will be
38 | automatically reloaded in the browser if you change the source code.
39 |
40 | ## Alternative way to use Girouette
41 |
42 | `Girouette` can be run directly inside your frontend application, where
43 | the generation of the CSS and injection in the browser's styles would be
44 | done entirely on the front end without the need to reload the CSS.
45 |
46 | This approach does not have a demo yet, feel free to contribute and add one.
47 |
--------------------------------------------------------------------------------
/example/reagent-demo/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src"]
2 |
3 | :deps {org.clojure/clojurescript {:mvn/version "1.11.60"}
4 | thheller/shadow-cljs {:mvn/version "2.19.5"}
5 | reagent/reagent {:mvn/version "1.1.1"}
6 | ;girouette/girouette {:local/root "../../lib/girouette"}
7 | girouette/girouette {:mvn/version "0.0.10"}}
8 |
9 | :aliases {; clojure -X:girouette-processor
10 | :girouette-processor {;:extra-deps {girouette/processor {:local/root "../../lib/processor"}}
11 | :extra-deps {girouette/processor {:mvn/version "0.0.8"}}
12 | :ns-default girouette.processor
13 | :exec-fn process
14 | :exec-args {:css {:output-file "public/style/girouette.css"}
15 | :base-css-rules [;girouette.tw.preflight/preflight-v2_0_3
16 | girouette.tw.preflight/preflight-v3_0_24
17 | acme.frontend.my-css/my-base-css-rules]
18 | :garden-fn acme.frontend.my-grammar/class-name->garden
19 | :apply-classes acme.frontend.my-css/composed-classes
20 | :watch? true
21 | #_#_:dry-run? true}}
22 |
23 | ; clojure -M:outdated --upgrade
24 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "1.8.847"}}
25 | :main-opts ["-m" "antq.core"]}}}
26 |
--------------------------------------------------------------------------------
/example/reagent-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reagent-demo",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "shadow-cljs": "2.19.5"
7 | },
8 | "dependencies": {
9 | "@radix-ui/react-icons": "^1.1.1",
10 | "react": "17.0.1",
11 | "react-dom": "17.0.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/reagent-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Acme frontend
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/reagent-demo/shadow-cljs.edn:
--------------------------------------------------------------------------------
1 | ;; shadow-cljs configuration
2 | {:deps true
3 |
4 | :dev-http
5 | {8080 "public"}
6 |
7 | :builds
8 | {:frontend
9 | {:target :browser
10 | :modules {:main {:init-fn acme.frontend.app/init}}}}}
11 |
--------------------------------------------------------------------------------
/example/reagent-demo/src/acme/frontend/annotated.cljc:
--------------------------------------------------------------------------------
1 | (ns acme.frontend.annotated
2 | (:require [girouette.core :refer [css]]))
3 |
4 | ;; -----------------------------------------------
5 | ;; Use {:retrieve :annotated} to collect those
6 | ;; -----------------------------------------------
7 |
8 | (css "text-green-300")
9 |
10 | ;; This might be supported by the Girouette processor in a next version.
11 | #_ (css (str "text-" "red" "-300"))
12 |
13 | (defn annotated-example []
14 | [:h1 {:class (css "flex")}
15 | [:div {:class (css "flex-10")} "hello"]
16 | [:div {:class (css "flex-20")} "the"]
17 | [:div {:class (css "flex-90/3")} "world"]])
18 |
--------------------------------------------------------------------------------
/example/reagent-demo/src/acme/frontend/app.cljc:
--------------------------------------------------------------------------------
1 | (ns acme.frontend.app
2 | (:require [reagent.dom :as rdom]
3 | ["@radix-ui/react-icons" :as Icons]))
4 |
5 | (defn simple-example []
6 | [:main
7 | [:div.bg-white.dark:bg-gray-800.p-10
8 | [:h1.text-3xl.text-gray-600.dark:text-gray-100 "Dark Mode Test"]]
9 |
10 | ;; Demonstrates the use of arbitrary values in flex layouts
11 | [:h1.flex.space-x-2
12 | [:div.flex-1.p-4.text-center.rounded-lg.bg-red-200 "hello"]
13 | [:div.flex-2.p-4.text-center.rounded-lg.bg-green-200 "the"]
14 | [:div.p-4.text-center.rounded-lg.bg-blue-200 {:class "flex-9/3"} "world"]]
15 |
16 | ;; Demonstrates the use of a custom Girouette component which provides the CSS class "rainbow-text"
17 | [:div.rainbow-text.text-center.font-size-10vw
18 | "Everybody needs a rainbow in their life"]
19 |
20 | ;; Demonstrates the use of custom colors (e.g. cat-white, cat-orange, cat-black)
21 | [:div.flex
22 | [:div.flex-1.bg-cat-white]
23 | [:div.flex-1.bg-cat-orange]
24 | [:div.flex-1.bg-cat-black]
25 | [:div.flex-1.p-4.text-center.text-4xl.text-cat-black.bg-cat-white "Miaw!!!"]
26 | [:div.flex-1.p-4.text-center.text-4xl.text-cat-white.bg-cat-orange "Miaw!!!"]
27 | [:div.flex-1.p-4.text-center.text-4xl.text-cat-orange.bg-cat-black "Miaw!!!"]]
28 |
29 | [:div.flex.my-10
30 | ;; Demonstrates a fix on the `divide-x` class.
31 | [:div.mx-auto.p-6.bg-gray-100
32 | [:div.flex.flex-row.bg-white.divide-x-10.divide-red-400-50
33 | [:div.p-3 "item 1"]
34 | [:div.p-3 "item 2"]
35 | [:div.p-3 {:hidden true} "item 3"]
36 | [:div.p-3 "item 4"]]]
37 |
38 | ;; Demonstrates the `divide-x-reverse` class.
39 | [:div.mx-auto.p-6.bg-gray-100
40 | [:div.flex.flex-row-reverse.bg-white.divide-x-10.divide-x-reverse.divide-red-400-50
41 | [:div.p-3 "reverse item 1"]
42 | [:div.p-3 "reverse item 2"]
43 | [:div.p-3 {:hidden true} "reverse item 3"]
44 | [:div.p-3 "reverse item 4"]]]]
45 |
46 | [:div.flex.my-10
47 | ;; Demonstrates a fix on the `divide-y` class.
48 | [:div.mx-auto.p-6.bg-gray-100
49 | [:div.flex.flex-col.bg-white.divide-y-10.divide-red-400-50
50 | [:div.p-3 "item 1"]
51 | [:div.p-3 "item 2"]
52 | [:div.p-3 {:hidden true} "item 3"]
53 | [:div.p-3 "item 4"]]]
54 |
55 | ;; Demonstrates the `divide-y-reverse` class.
56 | [:div.mx-auto.p-6.bg-gray-100
57 | [:div.flex.flex-col-reverse.bg-white.divide-y-10.divide-y-reverse.divide-red-400-50
58 | [:div.p-3 "reverse item 1"]
59 | [:div.p-3 "reverse item 2"]
60 | [:div.p-3 {:hidden true} "reverse item 3"]
61 | [:div.p-3 "reverse item 4"]]]]
62 |
63 | ;; Demonstrates the shadow colors
64 | [:div.m-4.p-4.grid.grid-cols-3.gap-4.justify-items-center.text-lg.border-1.rounded-lg
65 | [:p.font-medium.text-blueGray-500.font-mono.text-center.mb-3.dark:text-blueGray-400 "shadow-cyan-500/50"]
66 | [:p.font-medium.text-blueGray-500.font-mono.text-center.mb-3.dark:text-blueGray-400 "shadow-blue-500/50"]
67 | [:p.font-medium.text-blueGray-500.font-mono.text-center.mb-3.dark:text-blueGray-400 "shadow-indigo-500/50"]
68 | [:button.py-2.px-3.mb-4.bg-cyan-500.text-white.font-semibold.rounded-md.shadow-lg.shadow-cyan-500-50.focus:outline-none "Clojure rules"]
69 | [:button.py-2.px-3.mb-4.bg-blue-500.text-white.font-semibold.rounded-md.shadow-lg.shadow-blue-500-50.focus:outline-none "Clojure rules"]
70 | [:button.py-2.px-3.mb-4.bg-indigo-500.text-white.font-semibold.rounded-md.shadow-lg.shadow-indigo-500-50.focus:outline-none "Clojure rules"]]
71 |
72 | ;; Demonstrate the use of a native component.
73 | ;; The Girouette processor does not crash with a recent version of CLJS.
74 | [:p
75 | "Displays a native JS React component:"
76 | [:> Icons/CheckIcon]]])
77 |
78 | (defn render []
79 | (rdom/render [simple-example] (js/document.getElementById "app")))
80 |
81 | (defn init []
82 | (println "(init)")
83 | (render))
84 |
85 | (defn ^:dev/before-load stop []
86 | (println "(stop)"))
87 |
88 | (defn ^:dev/after-load start []
89 | (println "(start)")
90 | (render))
91 |
--------------------------------------------------------------------------------
/example/reagent-demo/src/acme/frontend/comprehensive.cljc:
--------------------------------------------------------------------------------
1 | (ns acme.frontend.comprehensive)
2 |
3 | ;; ---------------------------------------------------
4 | ;; Use {:retrieve :comprehensive} to collect those
5 | ;; (it's the default retrieval method)
6 | ;; ---------------------------------------------------
7 |
8 | (defn compact-example []
9 | [:h1.flex
10 | [:div.flex-1 "hello"]
11 | [:div.flex-2 "the"]
12 | [:div.flex-3 "world"]])
13 |
--------------------------------------------------------------------------------
/example/reagent-demo/src/acme/frontend/my_css.cljc:
--------------------------------------------------------------------------------
1 | (ns acme.frontend.my-css)
2 |
3 | ;; This CSS rules is appended to preflight by the Girouette processor.
4 | (def my-base-css-rules
5 | [;; Make the font bigger
6 | [:html {:font-size "20px"}]
7 |
8 | ;; More CSS rules can be added here
9 | ,])
10 |
11 |
12 | ;; This symbol is referenced in `deps.edn`
13 | (def composed-classes
14 | {"btn" ["p-2" "rounded"]
15 | "larger-btn" ["p-6" "rounded-xl"]})
16 |
--------------------------------------------------------------------------------
/example/reagent-demo/src/acme/frontend/my_grammar.cljc:
--------------------------------------------------------------------------------
1 | (ns acme.frontend.my-grammar
2 | (:require
3 | [girouette.version :as version]
4 | [girouette.tw.core :as gtw]
5 | [girouette.tw.common :as common]
6 | [girouette.tw.color :as color]
7 | [girouette.tw.layout :as layout]
8 | [girouette.tw.flexbox :as flexbox]
9 | [girouette.tw.grid :as grid]
10 | [girouette.tw.box-alignment :as box-alignment]
11 | [girouette.tw.spacing :as spacing]
12 | [girouette.tw.sizing :as sizing]
13 | [girouette.tw.typography :as typography]
14 | [girouette.tw.background :as background]
15 | [girouette.tw.border :as border]
16 | [girouette.tw.effect :as effect]
17 | ; [girouette.tw.table :as table]
18 | ; [girouette.tw.animation :as animation]
19 | ; [girouette.tw.transform :as transform]
20 | ; [girouette.tw.interactivity :as interactivity]
21 | ; [girouette.tw.svg :as svg]
22 | ; [girouette.tw.accessibility :as accessibility]
23 | ,))
24 |
25 |
26 | (def my-custom-components
27 | [{:id :rainbow-text
28 | :rules "
29 | rainbow-text = <'rainbow-text'>
30 | "
31 | :garden (fn [_]
32 | {:background-image "linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red)"
33 | :background-clip "text"
34 | ;:-webkit-background-clip "text"
35 | :color "transparent"})}]);})}])
36 |
37 |
38 | (def my-chosen-components
39 | (-> [common/components
40 | layout/components
41 | flexbox/components
42 | grid/components
43 | box-alignment/components
44 | spacing/components
45 | sizing/components
46 | typography/components
47 | background/components
48 | border/components
49 | effect/components
50 | ;table/components
51 | ;animation/components
52 | ;transform/components
53 | ;interactivity/components
54 | ;svg/components
55 | ;accessibility/components
56 | ,]
57 | (version/filter-components-by-version [:tw 3])
58 | (into my-custom-components)))
59 |
60 |
61 | ;; Adds colors to the existing default ones.
62 | (def my-color-map
63 | (assoc color/tw-v3-unified-colors-extended
64 | "cat-white" "eeeeee"
65 | "cat-orange" "e58c56"
66 | "cat-black" "333333"))
67 |
68 |
69 | ;; This example shows how to Girouette on a custom grammar.
70 | ;; Here, we use only a subset of the Girouette components, and we add your own.
71 | (def class-name->garden
72 | (-> my-chosen-components
73 | (gtw/make-api {:color-map my-color-map
74 | :font-family-map typography/tw-v2-font-family-map})
75 | :class-name->garden))
76 |
--------------------------------------------------------------------------------
/lib/girouette/.gitignore:
--------------------------------------------------------------------------------
1 | .cpcache
2 | .idea/
3 | .nrepl-port
4 | *.jar
5 | *.iml
6 | .cljs_node_repl
7 | .clj-kondo
8 | node_modules
9 | out
10 |
11 | *~
12 | [#]*[#]
13 | .\#*
14 |
--------------------------------------------------------------------------------
/lib/girouette/bin/kaocha:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | clojure -M:test -m kaocha.runner "$@"
3 |
--------------------------------------------------------------------------------
/lib/girouette/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src"]
2 | :deps {instaparse/instaparse {:mvn/version "1.4.12"}
3 | garden/garden {:mvn/version "1.3.10"}}
4 | :aliases {:dev {:extra-deps {org.clojure/clojure {:mvn/version "1.11.1"}
5 | org.clojure/clojurescript {:mvn/version "1.11.60"}}}
6 | :test {:extra-paths ["test"]
7 | :extra-deps {lambdaisland/kaocha {:mvn/version "1.68.1059"}
8 | lambdaisland/kaocha-cljs {:mvn/version "1.0.107"}
9 | lambdaisland/kaocha-junit-xml {:mvn/version "0.0.76"}
10 | org.clojure/test.check {:mvn/version "1.1.1"}}}
11 |
12 | ; clojure -M:outdated --upgrade
13 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "1.8.847"}}
14 | :main-opts ["-m" "antq.core"]}
15 |
16 | :depstar {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
17 | :exec-fn hf.depstar/jar
18 | :exec-args {:sync-pom true
19 | :group-id "girouette"
20 | :artifact-id "girouette"
21 | :version "0.0.10"
22 | :jar "girouette.jar"}}}}
23 | ;; Memo for deploying a new release:
24 | ;; - Change the version above, then build the jar:
25 | ;; clojure -X:depstar
26 | ;; - add a tag "v0.x.y" to the latest commit and push to repo
27 | ;; - deploy:
28 | ;; mvn deploy:deploy-file -Dfile=girouette.jar -DpomFile=pom.xml -DrepositoryId=clojars -Durl=https://clojars.org/repo/
29 |
--------------------------------------------------------------------------------
/lib/girouette/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "girouette",
3 | "lockfileVersion": 2,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "devDependencies": {
8 | "isomorphic-ws": "^4.0.1",
9 | "ws": "^7.0.1"
10 | }
11 | },
12 | "node_modules/isomorphic-ws": {
13 | "version": "4.0.1",
14 | "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
15 | "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
16 | "dev": true
17 | },
18 | "node_modules/ws": {
19 | "version": "7.4.2",
20 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
21 | "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==",
22 | "dev": true,
23 | "engines": {
24 | "node": ">=8.3.0"
25 | }
26 | }
27 | },
28 | "dependencies": {
29 | "isomorphic-ws": {
30 | "version": "4.0.1",
31 | "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
32 | "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
33 | "dev": true
34 | },
35 | "ws": {
36 | "version": "7.4.2",
37 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
38 | "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==",
39 | "dev": true
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/girouette/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "isomorphic-ws": "^4.0.1",
4 | "ws": "^7.0.1"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/lib/girouette/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | jar
5 | girouette
6 | girouette
7 | 0.0.10
8 | girouette
9 |
10 | https://github.com/green-coder/girouette
11 | scm:git:git://github.com/green-coder/girouette.git
12 | scm:git:ssh://git@github.com/green-coder/girouette.git
13 | master
14 |
15 |
16 |
17 | org.clojure
18 | clojure
19 | 1.10.3
20 |
21 |
22 | instaparse
23 | instaparse
24 | 1.4.12
25 |
26 |
27 | garden
28 | garden
29 | 1.3.10
30 |
31 |
32 |
33 | src
34 |
35 |
36 |
37 | clojars
38 | https://repo.clojars.org/
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/core.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.core
2 | #?(:cljs (:require-macros girouette.core)))
3 |
4 | (defmacro css
5 | "This macro should be used as a way to tag the expressions where
6 | all the strings should be interpreted as css class names."
7 | [expr]
8 | expr)
9 |
10 | #_ (defmacro not-css
11 | "This macro should be used as a way to tag the expressions where
12 | none of the strings and keywords should be interpreted as css class names.
13 |
14 | It may be used inside an expression annotated by the `css` macro above."
15 | [expr]
16 | expr)
17 |
18 | #_ (defmacro hiccup
19 | "This macro should be used as a way to tag the expressions which are
20 | in the Hiccup format, hinting code processors at how to find css class names."
21 | [expr]
22 | expr)
23 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/garden/util.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.garden.util
2 | (:require [clojure.spec.alpha :as s]
3 | [clojure.walk :as walk]
4 | #?(:clj [garden.types]
5 | :cljs [garden.types :refer [CSSAtRule]])
6 | [girouette.util :as util])
7 | #?(:clj (:import (garden.types CSSAtRule))))
8 |
9 | (declare merge-rules)
10 |
11 | (defn- merge-similar-rules [[rule-type x y] values]
12 | (case rule-type
13 | :simple [x (apply merge values)]
14 | :pseudo-class [x [y (apply merge values)]]
15 | :media (assoc-in x [:value :rules] (merge-rules (apply concat values)))
16 | :unknown values))
17 |
18 | (defn- rule-info [rule]
19 | (condp s/valid? rule
20 | (s/tuple string? map?)
21 | {:ident [:simple (first rule)]
22 | :value (second rule)}
23 |
24 | (s/tuple string? (s/tuple keyword? map?))
25 | {:ident [:pseudo-class (first rule) (first (second rule))]
26 | :value (second (second rule))}
27 |
28 | (s/and (s/keys :req-un [::identifier ::value])
29 | #(= :media (:identifier %)))
30 | {:ident [:media (update rule :value dissoc :rules)]
31 | :value (get-in rule [:value :rules])}
32 |
33 | {:ident [:unknown rule]
34 | :value rule}))
35 |
36 | (defn merge-rules
37 | "Combine garden rules that have the same selectors."
38 | [rules]
39 | (->> rules
40 | (map rule-info)
41 | (util/group-by :ident :value)
42 | (map (fn [[ident values]]
43 | (merge-similar-rules ident values)))))
44 |
45 | (defn apply-class-rules
46 | "Returns a collection of garden rules defining `target-class-name` as an aggregation of `gi-garden-rules`.
47 | `target-class-name` is the dotted CSS class name which we want to define.
48 | `gi-garden-rules` is an ordered collection of garden rules generated by Girouette.
49 | `gi-class-names` is an ordered collection of the CSS dotted class-names defined in `gi-garden-rules`."
50 | [target-class-name gi-garden-rules gi-class-names]
51 | (->> (map (fn [rule rule-class-name]
52 | (walk/postwalk (fn [x]
53 | (if (= rule-class-name x)
54 | target-class-name
55 | x))
56 | rule))
57 | gi-garden-rules
58 | gi-class-names)
59 | merge-rules))
60 |
61 | (defn rule-comparator
62 | "Compares the Garden rules provided by Girouette,
63 | so they can be ordered correctly in a style file."
64 | [rule1 rule2]
65 | (let [is-media-query1 (and (instance? CSSAtRule rule1)
66 | (= (:identifier rule1) :media))
67 | is-media-query2 (and (instance? CSSAtRule rule2)
68 | (= (:identifier rule2) :media))]
69 | (compare [is-media-query1 (-> rule1 meta :girouette/component :ordering-level)]
70 | [is-media-query2 (-> rule2 meta :girouette/component :ordering-level)])))
71 |
72 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/grammar/hiccup_tag.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.grammar.hiccup-tag
2 | (:require [instaparse.core :as insta]))
3 |
4 | (def hiccup-tag-grammar "
5 | hiccup-tag = html-tag (<'#'> id | (<'.'> class-name))*
6 | html-tag = segment
7 | id = segment
8 | class-name = segment
9 | = #'[^\\.#]+'
10 | ")
11 |
12 | (def hiccup-tag-parser
13 | (insta/parser hiccup-tag-grammar))
14 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/accessibility.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.accessibility)
2 |
3 | (def components
4 | [{:id :screen-reader
5 | :since-version [:tw 2]
6 | :rules "
7 | screen-reader = 'sr-only' | 'not-sr-only'
8 | "
9 | :garden (fn [{[type] :component-data}]
10 | (case type
11 | "sr-only" {:position "absolute"
12 | :width "1px"
13 | :height "1px"
14 | :padding 0
15 | :margin "-1px"
16 | :overflow "hidden"
17 | :clip "rect(0,0,0,0)"
18 | :white-space "nowrap"
19 | :border-width 0}
20 | "not-sr-only" {:position "static"
21 | :width "auto"
22 | :height "auto"
23 | :padding 0
24 | :margin 0
25 | :overflow "visible"
26 | :clip "auto"
27 | :white-space "normal"}))}])
28 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/animation.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.animation
2 | (:require [garden.stylesheet :as gs]
3 | [girouette.tw.common :refer [value-unit->css]]))
4 |
5 | (def components
6 | [{:id :transition-property
7 | :since-version [:tw 2]
8 | :rules "
9 | transition-property = <'transition'> (<'-'> ('none' | 'all' | 'colors' |
10 | 'opacity' | 'shadow' | 'transform'))?
11 | "
12 | :garden (fn [{[type] :component-data}]
13 | (if (= type "none")
14 | {:transition-property "none"}
15 | {:transition-property ({"all" "all"
16 | nil "background-color,border-color,color,fill,stroke,opacity,box-shadow,transform"
17 | "colors" "background-color,border-color,color,fill,stroke"
18 | "opacity" "opacity"
19 | "shadow" "box-shadow"
20 | "transform" "transform"} type)
21 | :transition-timing-function "cubic-bezier(0.4,0,0.2,1)"
22 | :transition-duration "150ms"}))}
23 |
24 |
25 | {:id :transition-duration
26 | :since-version [:tw 2]
27 | :rules "
28 | transition-duration = <'duration-'> (number | time)
29 | "
30 | :garden (fn [{[duration] :component-data}]
31 | {:transition-duration (value-unit->css duration {:zero-unit "s"
32 | :number {:unit "ms"}})})}
33 |
34 |
35 | {:id :transition-timing-function
36 | :since-version [:tw 2]
37 | :rules "
38 | transition-timing-function = <'ease-'> ('linear' | 'in' | 'out' | 'in-out')
39 | "
40 | :garden (fn [{[type] :component-data}]
41 | {:transition-timing-function ({"linear" "linear"
42 | "in" "cubic-bezier(0.4,0,1,1)"
43 | "out" "cubic-bezier(0,0,0.2,1)"
44 | "in-out" "cubic-bezier(0.4,0,0.2,1)"} type)})}
45 |
46 |
47 | {:id :transition-delay
48 | :since-version [:tw 2]
49 | :rules "
50 | transition-delay = <'delay-'> (number | time)
51 | "
52 | :garden (fn [{[duration] :component-data}]
53 | {:transition-delay (value-unit->css duration {:zero-unit "s"
54 | :number {:unit "ms"}})})}
55 |
56 |
57 | {:id :animation
58 | :since-version [:tw 2]
59 | :rules "
60 | animation = <'animate-'> ('none' | 'spin' | 'ping' | 'pulse' | 'bounce')
61 | "
62 | ;; TODO: keyframes should be expressed outside of any prefix's scope
63 | ;; It is time to introduce grammar component's dependencies
64 | :garden (fn [{[type] :component-data}]
65 | (case type
66 | "none"
67 | {:animation "none"}
68 |
69 | "spin"
70 | [{:animation "spin 1s linear infinite"}
71 | (gs/at-keyframes
72 | "spin"
73 | [:from {:transform "rotate(0)"}]
74 | [:to {:transform "rotate(360deg)"}])]
75 |
76 | "ping"
77 | [{:animation "ping 1s cubic-bezier(0,0,0.2,1) infinite"}
78 | (gs/at-keyframes
79 | "ping"
80 | ["75%" "100%" {:transform "scale(2)"
81 | :opacity 0}])]
82 |
83 | "pulse"
84 | [{:animation "pulse 2s cubic-bezier(0.4,0,0.6,1) infinite"}
85 | (gs/at-keyframes
86 | "pulse"
87 | ["0%" "100%" {:opacity 1}]
88 | ["50%" {:opacity 0.5}])]
89 |
90 | "bounce"
91 | [{:animation "bounce 1s infinite"}
92 | (gs/at-keyframes
93 | "bounce"
94 | ["0%" "100%" {:transform "translateY(-25%)"
95 | :animation-timing-function "cubic-bezier(0.8,0,1,1)"}]
96 | ["50%" {:transform "translateY(0)"
97 | :animation-timing-function "cubic-bezier(0,0,0.2,1)"}])]))}])
98 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/background.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.background
2 | (:require [clojure.string :as str]
3 | [girouette.tw.common :refer [value-unit->css div-100 div-4 mul-100]]
4 | [girouette.tw.color :refer [as-transparent color->css]]))
5 |
6 | (def components
7 | [{:id :background-attachment
8 | :since-version [:tw 2]
9 | :rules "
10 | background-attachment = <'bg-'> ('fixed' | 'local' | 'scroll')
11 | "
12 | :garden (fn [{[attachment-type] :component-data}]
13 | {:background-attachment attachment-type})}
14 |
15 |
16 | {:id :background-clip
17 | :since-version [:tw 2]
18 | :rules "
19 | background-clip = <'bg-clip-'> ('border' | 'padding' | 'content' | 'text')
20 | "
21 | :garden (fn [{[clip-type] :component-data}]
22 | {:background-clip ({"border" "border-box"
23 | "padding" "padding-box"
24 | "content" "content-box"
25 | "text" "text"} clip-type)})}
26 |
27 |
28 | {:id :background-color
29 | :since-version [:tw 2]
30 | :rules "
31 | background-color = <'bg-'> color
32 | "
33 | :garden (fn [{[color] :component-data
34 | read-color :read-color}]
35 | (let [color (read-color color)]
36 | (if (string? color)
37 | {:background-color color}
38 | (let [[r g b a] color]
39 | (if (some? a)
40 | {:background-color (color->css color)}
41 | {:--gi-bg-opacity 1
42 | :background-color (color->css [r g b "var(--gi-bg-opacity)"])})))))
43 | :before-rules #{:background-opacity}}
44 |
45 |
46 | {:id :background-opacity
47 | :since-version [:tw 2]
48 | :rules "
49 | background-opacity = <'bg-opacity-'> number
50 | "
51 | :garden (fn [{[value] :component-data}]
52 | {:--gi-bg-opacity (value-unit->css value {:value-fn div-100})})}
53 |
54 |
55 | {:id :background-origin
56 | :since-version [:tw 3]
57 | :rules "
58 | background-origin = <'bg-origin-'> ('border' | 'padding' | 'content')
59 | "
60 | :garden (fn [{[value] :component-data}]
61 | {:background-origin (str value "-box")})}
62 |
63 |
64 | {:id :background-position
65 | :since-version [:tw 2]
66 | :rules "
67 | background-position = <'bg-'> ('top' | 'center' | 'bottom' |
68 | 'left-top' | 'left' | 'left-bottom' |
69 | 'right-top' | 'right' | 'right-bottom')
70 | "
71 | :garden (fn [{[position] :component-data}]
72 | {:background-position (str/escape position {\- \space})})}
73 |
74 |
75 | {:id :background-repeat
76 | :since-version [:tw 2]
77 | :rules "
78 | background-repeat = <'bg-'> ('repeat' | 'no-repeat' | 'repeat-x' | 'repeat-y' |
79 | <'repeat-'> 'round' | <'repeat-'> 'space')
80 | "
81 | :garden (fn [{[repeat-type] :component-data}]
82 | {:background-repeat repeat-type})}
83 |
84 |
85 | {:id :background-size
86 | :since-version [:tw 2]
87 | :rules "
88 | background-size = <'bg-'> ('auto' | 'cover' | 'contain' |
89 | <'size-'> background-size-length <'-'> background-size-length)
90 | = auto | number | length | length-unit | fraction | percentage
91 | "
92 | :garden (fn [{data :component-data
93 | :keys [unitless-length-conversion]}]
94 | (if (= (count data) 1)
95 | {:background-size (first data)}
96 | (let [[x y] data
97 | options {:zero-unit nil
98 | :number unitless-length-conversion
99 | :fraction {:unit "%"
100 | :value-fn mul-100}}]
101 | {:background-size [[(value-unit->css x options)
102 | (value-unit->css y options)]]})))}
103 |
104 |
105 | {:id :background-image
106 | :since-version [:tw 2]
107 | :rules "
108 | background-image = <'bg-'> 'none' |
109 | <'bg-gradient-to-'> ('tr' | 't' | 'tl' | 'l' | 'r' | 'bl' | 'b' | 'br')
110 | "
111 | :garden (fn [{[data] :component-data}]
112 | {:background-image
113 | (if (= data "none")
114 | "none"
115 | (let [direction (->> data
116 | (map {\t "top"
117 | \b "bottom"
118 | \l "left"
119 | \r "right"})
120 | (str/join " "))]
121 | (str "linear-gradient(to " direction ","
122 | "var(--gi-gradient-stops))")))})}
123 |
124 |
125 | {:id :gradient-color-from
126 | :since-version [:tw 2]
127 | :rules "
128 | gradient-color-from = <'from-'> color
129 | "
130 | :garden (fn [{[color] :component-data
131 | read-color :read-color}]
132 | (let [color (read-color color)
133 | transp-color (as-transparent color)]
134 | {:--gi-gradient-from (color->css color)
135 | :--gi-gradient-stops (str "var(--gi-gradient-from),"
136 | "var(--gi-gradient-to," (color->css transp-color) ")")}))
137 | :before-rules #{:gradient-color-via}}
138 |
139 |
140 | {:id :gradient-color-to
141 | :since-version [:tw 2]
142 | :rules "
143 | gradient-color-to = <'to-'> color
144 | "
145 | :garden (fn [{[color] :component-data
146 | read-color :read-color}]
147 | {:--gi-gradient-to (color->css (read-color color))})}
148 |
149 |
150 | {:id :gradient-color-via
151 | :since-version [:tw 2]
152 | :rules "
153 | gradient-color-via = <'via-'> color
154 | "
155 | :garden (fn [{[color] :component-data
156 | read-color :read-color}]
157 | (let [color (read-color color)
158 | transp-color (as-transparent color)]
159 | {:--gi-gradient-stops (str "var(--gi-gradient-from),"
160 | (color->css color) ","
161 | "var(--gi-gradient-to," (color->css transp-color) ")")}))}])
162 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/box_alignment.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.box-alignment)
2 |
3 | (def components
4 | [{:id :justify-content
5 | :since-version [:tw 2]
6 | :rules "
7 | justify-content = <'justify-'> ('start' | 'end' | 'center' | 'between' | 'around' | 'evenly')
8 | "
9 | :garden (fn [{[param] :component-data}]
10 | {:justify-content ({"start" "flex-start"
11 | "end" "flex-end"
12 | "center" "center"
13 | "between" "space-between"
14 | "around" "space-around"
15 | "evenly" "space-evenly"} param)})}
16 |
17 |
18 | {:id :justify-items
19 | :since-version [:tw 2]
20 | :rules "
21 | justify-items = <'justify-items-'> ('auto' | 'start' | 'end' | 'center' | 'stretch')
22 | "
23 | :garden (fn [{[param] :component-data}]
24 | {:justify-items param})}
25 |
26 |
27 | {:id :justify-self
28 | :since-version [:tw 2]
29 | :rules "
30 | justify-self = <'justify-self-'> ('auto' | 'start' | 'end' | 'center' | 'stretch')
31 | "
32 | :garden (fn [{[param] :component-data}]
33 | {:justify-self param})}
34 |
35 |
36 | {:id :align-content
37 | :since-version [:tw 2]
38 | :rules "
39 | align-content = <'content-'> ('start' | 'end' | 'center' | 'between' | 'around' | 'evenly')
40 | "
41 | :garden (fn [{[param] :component-data}]
42 | {:align-content ({"start" "flex-start"
43 | "end" "flex-end"
44 | "center" "center"
45 | "between" "space-between"
46 | "around" "space-around"
47 | "evenly" "space-evenly"} param)})}
48 |
49 |
50 | {:id :align-items
51 | :since-version [:tw 2]
52 | :rules "
53 | align-items = <'items-'> ('start' | 'end' | 'center' | 'baseline' | 'stretch')
54 | "
55 | :garden (fn [{[param] :component-data}]
56 | {:align-items ({"start" "flex-start"
57 | "end" "flex-end"
58 | "center" "center"
59 | "baseline" "baseline"
60 | "stretch" "stretch"} param)})}
61 |
62 |
63 | {:id :align-self
64 | :since-version [:tw 2]
65 | :rules "
66 | align-self = <'self-'> ('auto' | 'start' | 'end' | 'center' | 'stretch' | 'baseline')
67 | "
68 | :garden (fn [{[param] :component-data}]
69 | {:align-self ({"auto" "auto"
70 | "start" "flex-start"
71 | "end" "flex-end"
72 | "center" "center"
73 | "stretch" "stretch"
74 | "baseline" "baseline"} param)})}
75 |
76 |
77 | {:id :place-content
78 | :since-version [:tw 2]
79 | :rules "
80 | place-content = <'place-content-'> ('start' | 'end' | 'center' | 'between' | 'around' | 'evenly' | 'stretch')
81 | "
82 | :garden (fn [{[param] :component-data}]
83 | {:place-content ({"start" "start"
84 | "end" "end"
85 | "center" "center"
86 | "between" "space-between"
87 | "around" "space-around"
88 | "evenly" "space-evenly"
89 | "stretch" "stretch"} param)})}
90 |
91 |
92 | {:id :place-items
93 | :since-version [:tw 2]
94 | :rules "
95 | place-items = <'place-items-'> ('auto' | 'start' | 'end' | 'center' | 'stretch')
96 | "
97 | :garden (fn [{[param] :component-data}]
98 | {:place-items param})}
99 |
100 |
101 | {:id :place-self
102 | :since-version [:tw 2]
103 | :rules "
104 | place-self = <'place-self-'> ('auto' | 'start' | 'end' | 'center' | 'stretch')
105 | "
106 | :garden (fn [{[param] :component-data}]
107 | {:place-self param})}])
108 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/common.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.common
2 | (:require [clojure.string :as str]
3 | [clojure.edn :as edn]
4 | [garden.selectors]
5 | [garden.stylesheet :as gs]))
6 |
7 | ;; This Instaparse grammar matches nothing.
8 | ;; It literally means "look ahead to see 'nop', then see 'no-way'".
9 | (def matches-nothing "&'nop' 'no-way'")
10 |
11 | (defn- state-variant->str [state-variant]
12 | (cond
13 | (string? state-variant)
14 | (str ":"
15 | ({"first" "first-child"
16 | "last" "last-child"
17 | "odd" "nth-child(odd)"
18 | "even" "nth-child(even)"} state-variant state-variant))
19 |
20 | (and (coll? state-variant)
21 | (= :attribute-state-variant (first state-variant)))
22 | (str "[" (second state-variant) "]")))
23 |
24 |
25 | (defn- target-variant->str [target-variant]
26 | (str "::" ({"file" "file-selector-button"} target-variant target-variant)))
27 |
28 |
29 | (defn- outer-state-variants
30 | [variant]
31 | (and (coll? variant)
32 | (#{:group-state-variant :peer-state-variant} (first variant))))
33 |
34 | (defn dot [class-name]
35 | (str "." (str/replace class-name #"[^-a-zA-Z0-9_]"
36 | (fn [c] (str "\\" c)))))
37 |
38 |
39 | (def breakpoint->pixels
40 | {"sm" "640px"
41 | "md" "768px"
42 | "lg" "1024px"
43 | "xl" "1280px"
44 | "2xl" "1536px"})
45 |
46 |
47 | (defn div-4 [x] (/ x 4))
48 | (defn div-100 [x] (/ x 100))
49 | (defn mul-4 [x] (* x 4))
50 | (defn mul-100 [x] (* x 100))
51 | (defn mul-255 [x] (* x 255))
52 | (defn clamp-0-255 [x] (-> x (max 0) (min 255)))
53 | (defn ratio-str [[x y]] (str x " / " y))
54 |
55 |
56 | (defn read-number
57 | "Converts the input into a number.
58 | Accepts the formats [:integer \"1\"], [:number \"1\"] or \"1\".
59 | This function might become private at some point, do not use if possible."
60 | [data]
61 | (let [number-str (cond-> data (vector? data) second)]
62 | (-> number-str
63 | (str/escape {\_ \.})
64 | edn/read-string)))
65 |
66 |
67 | (defn number->double-or-int
68 | "Convert numeric value to a double, or an int if the value can be converted without a loss.
69 | Useful for getting rid of ratio numbers like 5/2."
70 | [value]
71 | (if (= (double (int value))
72 | (double value))
73 | (int value)
74 | (double value)))
75 |
76 |
77 | (defn value-unit->css
78 | ([data]
79 | (value-unit->css data {}))
80 | ;; The options also contain :unit, :zero-unit and :value-fn, at the root and
81 | ;; can also contain an override per data-type, e.g. {:fraction {:unit ...}}
82 | ([data {:keys [signus] :as options}]
83 | (case (first data)
84 | :auto "auto"
85 | :none "none"
86 | :full "full"
87 | :min-content "min-content"
88 | :max-content "max-content"
89 | :fit-content "fit-content"
90 | (let [[data-type arg1 arg2] data
91 | [value unit] (case data-type
92 | :integer [(read-number arg1) nil]
93 | :number [(read-number arg1) nil]
94 | :length [(read-number arg1) arg2]
95 | :length-unit [1 arg1]
96 | :angle [(read-number arg1) arg2]
97 | :time [(read-number arg1) arg2]
98 | :percentage [(read-number arg1) "%"]
99 | :fraction [(/ (read-number arg1) (read-number arg2)) nil]
100 | :ratio [[(read-number arg1) (read-number arg2)] nil]
101 | :full-100% [100 "%"]
102 | :screen-100vw [100 "vw"]
103 | :screen-100vh [100 "vh"])
104 | value-fn (get-in options [data-type :value-fn] (:value-fn options identity))
105 | value (value-fn value)
106 | unit (get-in options [data-type :unit] (:unit options unit))
107 | zero-unit (get-in options [data-type :zero-unit] (:zero-unit options unit))
108 | [value unit] (if (and (number? value)
109 | (zero? value))
110 | [0 zero-unit]
111 | [value unit])
112 | value (cond-> value (= signus "-") (* -1))]
113 | (cond-> value
114 | (number? value) number->double-or-int
115 | (some? unit) (str unit))))))
116 |
117 |
118 | (defn inner-state-variants-transform [rule props]
119 | (reduce (fn [rule [variant-type variant-value]]
120 | [(keyword (str "&"
121 | (case variant-type
122 | :target-variant
123 | (target-variant->str variant-value)
124 |
125 | (:plain-state-variant :attribute-state-variant)
126 | (state-variant->str variant-value))))
127 | rule])
128 | rule
129 | (->> props :prefixes :state-variants reverse
130 | (remove outer-state-variants))))
131 |
132 |
133 | (defn class-name-transform [rule props]
134 | [(dot (:class-name props)) rule])
135 |
136 |
137 | (def between-children-selector
138 | "Selects every direct child of an element except the last.
139 | Commonly used to visually style 'between' a list of elements.
140 |
141 | For example:
142 | .space-y-2 uses this selector to add space between elements"
143 | (garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])"))
144 |
145 |
146 | (defn outer-state-variants-transform [rule props]
147 | (reduce (fn [rule state-variant]
148 | (case (first state-variant)
149 | :group-state-variant
150 | [(str ".group" (state-variant->str (second state-variant)))
151 | rule]
152 |
153 | :peer-state-variant
154 | (into [(:selector
155 | (garden.selectors/-
156 | (str ".peer" (state-variant->str (second state-variant)))
157 | (first rule)))]
158 | (rest rule))))
159 | rule
160 | (->> props :prefixes :state-variants reverse
161 | (filter outer-state-variants))))
162 |
163 |
164 | (defn media-queries-transform [rule props]
165 | (let [prefixes (:prefixes props)
166 | min-width (-> prefixes :media-query-min-width breakpoint->pixels)
167 | color-scheme (-> prefixes :media-query-color-scheme)
168 | reduced-motion (-> prefixes :media-query-reduced-motion {"motion-safe" "no-preference"
169 | "motion-reduce" "reduced"})
170 | orientation (-> prefixes :media-query-orientation)
171 | media-query (cond-> {}
172 | min-width (assoc :min-width min-width)
173 | color-scheme (assoc :prefers-color-scheme color-scheme)
174 | reduced-motion (assoc :prefers-reduced-motion reduced-motion)
175 | orientation (assoc :orientation orientation))]
176 | (if (seq media-query)
177 | (gs/at-media media-query rule)
178 | rule)))
179 |
180 |
181 | (def default-pipeline
182 | {:media-queries [media-queries-transform]
183 | :outer-state-variants [outer-state-variants-transform]
184 | :class-name [class-name-transform]
185 | :inner-state-variants [inner-state-variants-transform]})
186 |
187 |
188 | (def common-rules "
189 | prefixes = (media-query <':'>)* (state-variant <':'>)*
190 |
191 | = media-query-min-width |
192 | media-query-color-scheme |
193 | media-query-reduced-motion |
194 | media-query-orientation
195 | media-query-min-width = 'sm' | 'md' | 'lg' | 'xl' | '2xl'
196 | media-query-color-scheme = 'light' | 'dark'
197 | media-query-reduced-motion = 'motion-safe' | 'motion-reduce'
198 | media-query-orientation = 'landscape' | 'portrait'
199 |
200 | attribute-state-variant = 'open'
201 | = 'hover' | 'focus' | 'disabled' | 'active' |
202 | 'focus-within' | 'focus-visible' |
203 | 'any-link' | 'link' | 'visited' | 'target' |
204 | 'blank' | 'required' | 'optional' | 'valid' | 'invalid' | 'placeholder-shown' | 'checked' |
205 | 'read-only' | 'read-write' |
206 | 'first' | 'last' | 'odd' | 'even' | 'first-of-type' | 'last-of-type' |
207 | 'root' | 'empty' |
208 | attribute-state-variant
209 | group-state-variant = <'group-'> state-variant-value
210 | peer-state-variant = <'peer-'> state-variant-value
211 | plain-state-variant = state-variant-value
212 | target-variant = 'file' | 'before' | 'after' | 'placeholder'
213 | state-variant = group-state-variant | peer-state-variant | plain-state-variant | target-variant
214 |
215 | signus = '-' | '+'
216 | direction = 't' | 'r' | 'b' | 'l'
217 | axis = 'x' | 'y'
218 |
219 | fraction = integer <'/'> integer
220 | ratio = integer <'/'> integer
221 |
222 | full-100% = 'full'
223 | screen-100vw = 'screen'
224 | screen-100vh = 'screen'
225 | min-content = 'min'
226 | max-content = 'max'
227 | fit-content = 'fit'
228 | auto = 'auto'
229 | none = 'none'
230 | full = 'full'
231 |
232 | (* source: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units *)
233 | integer = #'\\d+'
234 | number = #'\\d+([._]\\d+)?'
235 | percentage = number <'%'>
236 |
237 | length = number (absolute-length-unit | relative-length-unit)
238 | length-unit = absolute-length-unit | relative-length-unit
239 | = 'cm' | 'mm' | 'in' | 'pc' | 'pt' | 'px'
240 | = 'em' | 'ex' | 'ch' | 'rem' | 'lh' | 'vw' | 'vh' | 'vmin' | 'vmax'
241 |
242 | angle = number angle-unit
243 | = 'deg' | 'grad' | 'rad' | 'turn'
244 |
245 | time = number time-unit
246 | = 's' | 'ms'
247 |
248 | resolution = number resolution-unit
249 | = 'dpi' | 'dpcm' | 'dppx' | 'x'
250 |
251 | ")
252 |
253 | (def components
254 | [{:id :arbitrary-property
255 | :since-version [:tw 3]
256 | :rules "
257 | arbitrary-property = <'['> #'[^\\] ]*' <']'>
258 | "
259 | :garden (fn [{[value] :component-data}]
260 | (let [[prop val] (-> value
261 | ;; negative-lookbehind isn't supported in javascript
262 | (str/replace #"(^|[^\\])_" "$1 ")
263 | (str/replace #"\\_" "_")
264 | (str/split #":" 2))]
265 | {(keyword prop) val}))}])
266 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/core.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.core
2 | (:require
3 | [clojure.string :as str]
4 | [clojure.set :as set]
5 | [instaparse.core :as insta]
6 | [girouette.util :as util]
7 | [girouette.tw.common :as common]
8 | [girouette.tw.color :as color]
9 | [girouette.tw.typography :as typography]))
10 |
11 |
12 | (defn- assemble-grammar [components {:keys [color-map font-family-map]}]
13 | (let [root-rule (str "css-class = prefixes ("
14 | (->> components
15 | (map (comp name :id))
16 | (str/join " | "))
17 | ")\n")]
18 | (->> (concat
19 | [root-rule]
20 | (map :rules components)
21 | [common/common-rules
22 | (color/color-rules color-map)
23 | (typography/font-family-rules font-family-map)])
24 | (apply str))))
25 |
26 |
27 | (defn- parsed-data->props [class-name parsed-data predef-props]
28 | (let [[_
29 | [_ & prefixes]
30 | [component-id & component-data]] parsed-data
31 | {[media-query-min-width] :media-query-min-width
32 | [media-query-color-scheme] :media-query-color-scheme
33 | [media-query-reduced-motion] :media-query-reduced-motion
34 | [media-query-orientation] :media-query-orientation
35 | state-variants :state-variant} (util/group-by first second prefixes)]
36 | (assoc predef-props
37 | :class-name class-name
38 | :prefixes {:media-query-min-width media-query-min-width
39 | :media-query-color-scheme media-query-color-scheme
40 | :media-query-reduced-motion media-query-reduced-motion
41 | :media-query-orientation media-query-orientation
42 | :state-variants state-variants}
43 | :component-id component-id
44 | :component-data (vec component-data))))
45 |
46 |
47 | (defn- pipeline->transform [pipeline]
48 | (fn [rule props]
49 | (reduce (fn [rule f] (f rule props))
50 | rule
51 | (->> pipeline
52 | ((juxt :media-queries
53 | :outer-state-variants
54 | :class-name
55 | :inner-state-variants))
56 | (apply concat)
57 | reverse))))
58 |
59 |
60 | (defn- complement-before-rules-after-rules
61 | "Ensures that each :before-rules relation has a :after-rules relation, and vice-versa."
62 | [id->component]
63 | (reduce (fn [id->component [id {:keys [before-rules after-rules]}]]
64 | (let [;; Ensure the presence of all :after-rules from :before-rules
65 | id->component (reduce (fn [id->component before-id]
66 | (update-in id->component [before-id :after-rules]
67 | (fnil conj #{}) id))
68 | id->component
69 | before-rules)
70 | ;; Ensure the presence of all :before-rules from :after-rules
71 | id->component (reduce (fn [id->component after-id]
72 | (update-in id->component [after-id :before-rules]
73 | (fnil conj #{}) id))
74 | id->component
75 | after-rules)]
76 | id->component))
77 | id->component
78 | id->component))
79 |
80 |
81 | (defn- assoc-ordering-level
82 | "Sorts the components typologically and assign them an ordering level in the graph."
83 | [id->component]
84 | (loop [id->component id->component
85 | visited-ids #{}
86 | level 0
87 | candidates-ids (keys id->component)]
88 | (if (empty? candidates-ids)
89 | id->component
90 | (let [root-ids (into #{}
91 | (filter (fn [id]
92 | (empty? (set/difference (get-in id->component [id :after-rules])
93 | visited-ids))))
94 | candidates-ids)
95 | id->component (reduce (fn [id->component id]
96 | (assoc-in id->component [id :ordering-level] level))
97 | id->component
98 | root-ids)
99 | visited-ids (into visited-ids root-ids)
100 | level (inc level)
101 | candidates-ids (into #{}
102 | (mapcat (fn [id]
103 | (get-in id->component [id :before-rules])))
104 | root-ids)]
105 | (recur id->component visited-ids level candidates-ids)))))
106 |
107 |
108 | (defn make-api
109 | "Creates an API based on a collection of Girouette components."
110 | [components {:keys [color-map
111 | font-family-map
112 | unitless-length-conversion]
113 | :or {unitless-length-conversion {:unit "rem"
114 | :value-fn common/div-4}}}]
115 | (let [components (util/into-one-vector components) ;; flatten the structure
116 | flattened-color-map (color/flatten-color-map color-map)
117 | grammar (assemble-grammar components {:color-map flattened-color-map
118 | :font-family-map font-family-map})
119 | parser (insta/parser grammar)
120 | component-by-id (-> (into {}
121 | (map (juxt :id identity))
122 | components)
123 | complement-before-rules-after-rules
124 | assoc-ordering-level)
125 | predef-props {:read-color (partial color/read-color flattened-color-map)
126 | :font-family-map font-family-map
127 | :unitless-length-conversion unitless-length-conversion}
128 | class-name->garden (fn [class-name]
129 | (let [parsed-data (insta/parse parser class-name)]
130 | (when-not (insta/failure? parsed-data)
131 | (let [props (parsed-data->props class-name parsed-data predef-props)
132 | component (component-by-id (:component-id props))
133 | garden-fn (:garden component)
134 | pipeline (:pipeline component common/default-pipeline)
135 | transform (pipeline->transform pipeline)]
136 | (-> (garden-fn props)
137 | (transform props)
138 | (with-meta {:girouette/props props
139 | :girouette/component component
140 | :girouette/component-by-id component-by-id}))))))]
141 | {:grammar grammar
142 | :parser parser
143 | :component-by-id component-by-id
144 | :class-name->garden class-name->garden}))
145 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/default_api.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.default-api
2 | (:require
3 | [girouette.tw.core :as gtw]
4 | [girouette.version :as version]
5 | [girouette.tw.common :as common]
6 | [girouette.tw.color :as color]
7 | [girouette.tw.layout :as layout]
8 | [girouette.tw.flexbox :as flexbox]
9 | [girouette.tw.grid :as grid]
10 | [girouette.tw.box-alignment :as box-alignment]
11 | [girouette.tw.spacing :as spacing]
12 | [girouette.tw.sizing :as sizing]
13 | [girouette.tw.typography :as typography]
14 | [girouette.tw.background :as background]
15 | [girouette.tw.border :as border]
16 | [girouette.tw.effect :as effect]
17 | [girouette.tw.filter :as filter]
18 | [girouette.tw.table :as table]
19 | [girouette.tw.animation :as animation]
20 | [girouette.tw.transform :as transform]
21 | [girouette.tw.interactivity :as interactivity]
22 | [girouette.tw.svg :as svg]
23 | [girouette.tw.accessibility :as accessibility]))
24 |
25 | (def all-tw-components
26 | [common/components
27 | layout/components
28 | flexbox/components
29 | grid/components
30 | box-alignment/components
31 | spacing/components
32 | sizing/components
33 | typography/components
34 | background/components
35 | border/components
36 | effect/components
37 | filter/components
38 | table/components
39 | animation/components
40 | transform/components
41 | interactivity/components
42 | svg/components
43 | accessibility/components])
44 |
45 |
46 | ;; The API built using the Tailwind v2 components.
47 | (let [{:keys [parser class-name->garden]} (-> all-tw-components
48 | (version/filter-components-by-version [:tw 2])
49 | (gtw/make-api {:color-map color/tw-v2-colors
50 | :font-family-map typography/tw-v2-font-family-map}))]
51 | (def tw-v2-parser parser)
52 | (def tw-v2-class-name->garden class-name->garden))
53 |
54 |
55 | ;; The API built using the Tailwind v3 components.
56 | (let [{:keys [parser class-name->garden]} (-> all-tw-components
57 | (version/filter-components-by-version [:tw 3])
58 | (gtw/make-api {:color-map color/tw-v3-unified-colors-extended
59 | :font-family-map typography/tw-v2-font-family-map
60 | ;; Customization of unitless-length conversion is possible.
61 | ;; e.g. "p-2" -> {:padding "8px"}
62 | #_#_
63 | :unitless-length-conversion {:unit "px"
64 | :value-fn common/mul-4}}))]
65 | (def tw-v3-parser parser)
66 | (def tw-v3-class-name->garden class-name->garden))
67 |
68 |
69 | ;; Feel free to fork the snippet above and add your own components,
70 | ;; as that's what Girouette was made for: customization.
71 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/effect.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.effect
2 | (:require [clojure.string :as str]
3 | [girouette.tw.common :refer [value-unit->css div-100]]
4 | [girouette.tw.color :refer [color->css]]))
5 |
6 | (def components
7 | [{:id :box-shadow
8 | :since-version [:tw 2]
9 | :removed-in-version [:tw 3]
10 | :rules "
11 | box-shadow = <'shadow'> (<'-'> box-shadow-value)?
12 | box-shadow-value = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'inner' | 'none'
13 | "
14 | :garden (fn [{data :component-data}]
15 | (let [{:keys [box-shadow-value]} (into {} data)
16 | shadow-params (case box-shadow-value
17 | "sm" "0 1px 2px 0 rgba(0,0,0,0.05)"
18 | nil (str "0 1px 3px 0 rgba(0,0,0,0.1),"
19 | "0 1px 2px 0 rgba(0,0,0,0.06)")
20 | "md" (str "0 4px 6px -1px rgba(0,0,0,0.1),"
21 | "0 2px 4px -1px rgba(0,0,0,0.06)")
22 | "lg" (str "0 10px 15px -3px rgba(0,0,0,0.1),"
23 | "0 4px 6px -2px rgba(0,0,0,0.05)")
24 | "xl" (str "0 20px 25px -5px rgba(0,0,0,0.1),"
25 | "0 10px 10px -5px rgba(0,0,0,0.04)")
26 | "2xl" "0 25px 50px -12px rgba(0,0,0,0.25)"
27 | "inner" "inset 0 2px 4px 0 rgba(0,0,0,0.06)"
28 | "none" "0 0 #0000")]
29 | {:--gi-shadow shadow-params
30 | :box-shadow (str "var(--gi-ring-offset-shadow,0 0 #0000),"
31 | "var(--gi-ring-shadow,0 0 #0000),"
32 | "var(--gi-shadow)")}))}
33 |
34 | {:id :box-shadow
35 | :since-version [:tw 3]
36 | :rules "
37 | box-shadow = <'shadow'> (<'-'> box-shadow-value)?
38 | box-shadow-value = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'inner' | 'none'
39 | "
40 | :garden (fn [{data :component-data}]
41 | (let [{:keys [box-shadow-value]} (into {} data)
42 | [shadows-params shadow-color] (case box-shadow-value
43 | "sm" [["0 1px 2px 0"] "rgba(0,0,0,0.05)"]
44 | nil [["0 1px 3px 0"
45 | "0 1px 2px -1px"] "rgba(0,0,0,0.1)"]
46 | "md" [["0 4px 6px -1px"
47 | "0 2px 4px -2px"] "rgba(0,0,0,0.1)"]
48 | "lg" [["0 10px 15px -3px"
49 | "0 4px 6px -4px"] "rgba(0,0,0,0.1)"]
50 | "xl" [["0 20px 25px -5px"
51 | "0 8px 10px -6px"] "rgba(0,0,0,0.1)"]
52 | "2xl" [["0 25px 50px -12px"] "rgba(0,0,0,0.25)"]
53 | "inner" [["inset 0 2px 4px 0"] "rgba(0,0,0,0.05)"]
54 | "none" [["0 0"] "#0000"])]
55 | {:--gi-shadow (->> shadows-params
56 | (map (fn [shadow-params]
57 | (str shadow-params " " shadow-color)))
58 | (str/join ","))
59 | :--gi-shadow-colored (->> shadows-params
60 | (map (fn [shadow-params]
61 | (str shadow-params " var(--gi-shadow-color)")))
62 | (str/join ","))
63 | :box-shadow (str "var(--gi-ring-offset-shadow,0 0 #0000),"
64 | "var(--gi-ring-shadow,0 0 #0000),"
65 | "var(--gi-shadow)")}))}
66 |
67 |
68 | {:id :box-shadow-color
69 | :since-version [:tw 2]
70 | :rules "
71 | box-shadow-color = <'shadow-'> ('inherit' | color)
72 | "
73 | :garden (fn [{[color] :component-data
74 | read-color :read-color}]
75 | {:--gi-shadow-color (if (= color "inherit")
76 | "inherit"
77 | (color->css (read-color color)))
78 | :--gi-shadow "var(--gi-shadow-colored)"})}
79 |
80 |
81 | {:id :opacity
82 | :since-version [:tw 2]
83 | :rules "
84 | opacity = <'opacity-'> (number | percentage | fraction)
85 | "
86 | :garden (fn [{[value] :component-data}]
87 | {:opacity (value-unit->css value {:number {:value-fn div-100}})})}
88 |
89 |
90 | {:id :mix-blend-mode
91 | :since-version [:tw 3]
92 | :rules "
93 | mix-blend-mode = <'mix-blend-'> mix-blend-mode-value
94 | = 'normal' | 'multiply' | 'screen' | 'overlay' |
95 | 'darken' | 'lighten' | 'color-dodge' |
96 | 'color-burn' | 'hard-light' | 'soft-light' |
97 | 'difference' | 'exclusion' | 'hue' | 'saturation' |
98 | 'color' | 'luminosity'
99 | "
100 | :garden (fn [{[blend-mode] :component-data}]
101 | {:mix-blend-mode blend-mode})}
102 |
103 |
104 | {:id :background-blend-mode
105 | :since-version [:tw 3]
106 | :rules "
107 | background-blend-mode = <'bg-blend-'> background-blend-mode-value
108 | = 'normal' | 'multiply' | 'screen' | 'overlay' |
109 | 'darken' | 'lighten' | 'color-dodge' |
110 | 'color-burn' | 'hard-light' | 'soft-light' |
111 | 'difference' | 'exclusion' | 'hue' | 'saturation' |
112 | 'color' | 'luminosity'
113 | "
114 | :garden (fn [{[blend-mode] :component-data}]
115 | {:background-blend-mode blend-mode})}])
116 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/filter.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.filter
2 | (:require [girouette.tw.common :refer [value-unit->css div-100 mul-100]]))
3 |
4 | (def ^:private filter-rule
5 | (str "var(--gi-blur, ) var(--gi-brightness, ) var(--gi-contrast, ) "
6 | "var(--gi-grayscale, ) var(--gi-hue-rotate, ) var(--gi-invert, ) "
7 | "var(--gi-saturate, ) var(--gi-sepia, ) var(--gi-drop-shadow, )"))
8 |
9 | (def ^:private backdrop-filter-rule
10 | (str "var(--gi-backdrop-blur) var(--gi-backdrop-brightness) var(--gi-backdrop-contrast) "
11 | "var(--gi-backdrop-grayscale) var(--gi-backdrop-hue-rotate) var(--gi-backdrop-invert) "
12 | "var(--gi-backdrop-opacity) var(--gi-backdrop-saturate) var(--gi-backdrop-sepia)"))
13 |
14 | (def components
15 | [{:id :blur
16 | :since-version [:tw 3]
17 | :rules "
18 | blur = <'blur'> (<'-'> (blur-value-fix | blur-value-number))?
19 | blur-value-fix = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'none'
20 | blur-value-number = length
21 | "
22 | :garden (fn [{value :component-data}]
23 | (let [blur (let [{:keys [blur-value-number blur-value-fix]} (into {} value)]
24 | (if (some? blur-value-number)
25 | (value-unit->css blur-value-number)
26 | ({"none" "0"
27 | "sm" "4px"
28 | nil "8px"
29 | "md" "12px"
30 | "lg" "16px"
31 | "xl" "24px"
32 | "2xl" "40px"
33 | "3xl" "64px"} blur-value-fix)))]
34 | {:--gi-blur (str "blur(" blur ")")
35 | :filter filter-rule}))}
36 |
37 |
38 | {:id :brightness
39 | :since-version [:tw 3]
40 | :rules "
41 | brightness = <'brightness-'> (number | percentage | fraction)
42 | "
43 | :garden (fn [{[value] :component-data}]
44 | {:--gi-brightness (str "brightness("
45 | (value-unit->css value {:number {:value-fn div-100}})
46 | ")")
47 | :filter filter-rule})}
48 |
49 |
50 | {:id :contrast
51 | :since-version [:tw 3]
52 | :rules "
53 | contrast = <'contrast-'> (number | percentage | fraction)
54 | "
55 | :garden (fn [{[value] :component-data}]
56 | {:--gi-contrast (str "contrast("
57 | (value-unit->css value {:number {:value-fn div-100}})
58 | ")")
59 | :filter filter-rule})}
60 |
61 |
62 | {:id :drop-shadow
63 | :since-version [:tw 3]
64 | :rules "
65 | drop-shadow = <'drop-shadow'> (<'-'> drop-shadow-value)?
66 | = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'none'
67 | "
68 | :garden (fn [{[value] :component-data}]
69 | {:--gi-drop-shadow (case value
70 | "sm" "drop-shadow(0 1px 1px rgb(0 0 0 / 0.05))"
71 | nil "drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06))"
72 | "md" "drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06))"
73 | "lg" "drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1))"
74 | "xl" "drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08))"
75 | "2xl" "drop-shadow(0 25px 25px rgb(0 0 0 / 0.15))"
76 | "none" "drop-shadow(0 0 #0000)")
77 | :filter filter-rule})}
78 |
79 |
80 | {:id :grayscale
81 | :since-version [:tw 3]
82 | :rules "
83 | grayscale = <'grayscale'> (<'-'> (number | percentage | fraction))?
84 | "
85 | :garden (fn [{[value] :component-data}]
86 | {:--gi-grayscale (str "grayscale("
87 | (if (nil? value)
88 | "100%"
89 | (value-unit->css value {:number {:value-fn div-100}}))
90 | ")")
91 | :filter filter-rule})}
92 |
93 |
94 | {:id :hue-rotate
95 | :since-version [:tw 3]
96 | :rules "
97 | hue-rotate = <'hue-rotate-'> (number | angle)
98 | "
99 | :garden (fn [{[value] :component-data}]
100 | {:--gi-hue-rotate (str "hue-rotate("
101 | (value-unit->css value {:number {:unit "deg"}})
102 | ")")
103 | :filter filter-rule})}
104 |
105 |
106 | {:id :invert
107 | :since-version [:tw 3]
108 | :rules "
109 | invert = <'invert'> (<'-'> (number | percentage | fraction))?
110 | "
111 | :garden (fn [{[value] :component-data}]
112 | {:--gi-invert (str "invert("
113 | (if (nil? value)
114 | "100%"
115 | (value-unit->css value {:number {:value-fn div-100}}))
116 | ")")
117 | :filter filter-rule})}
118 |
119 |
120 | {:id :saturate
121 | :since-version [:tw 3]
122 | :rules "
123 | saturate = <'saturate-'> (number | percentage | fraction)
124 | "
125 | :garden (fn [{[value] :component-data}]
126 | {:--gi-saturate (str "saturate("
127 | (value-unit->css value {:number {:value-fn div-100}})
128 | ")")
129 | :filter filter-rule})}
130 |
131 |
132 | {:id :sepia
133 | :since-version [:tw 3]
134 | :rules "
135 | sepia = <'sepia'> (<'-'> (number | percentage | fraction))?
136 | "
137 | :garden (fn [{[value] :component-data}]
138 | {:--gi-sepia (str "sepia("
139 | (if (nil? value)
140 | "100%"
141 | (value-unit->css value {:number {:value-fn div-100}}))
142 | ")")
143 | :filter filter-rule})}
144 |
145 |
146 | {:id :backdrop-blur
147 | :since-version [:tw 3]
148 | :rules "
149 | backdrop-blur = <'backdrop-blur'> (<'-'> (backdrop-blur-value-fix | backdrop-blur-value-number))?
150 | backdrop-blur-value-fix = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'none'
151 | backdrop-blur-value-number = length
152 | "
153 | :garden (fn [{value :component-data}]
154 | (let [blur (let [{:keys [backdrop-blur-value-number
155 | backdrop-blur-value-fix]} (into {} value)]
156 | (if (some? backdrop-blur-value-number)
157 | (value-unit->css backdrop-blur-value-number)
158 | ({"none" "0"
159 | "sm" "4px"
160 | nil "8px"
161 | "md" "12px"
162 | "lg" "16px"
163 | "xl" "24px"
164 | "2xl" "40px"
165 | "3xl" "64px"} backdrop-blur-value-fix)))]
166 | {:--gi-backdrop-blur (str "blur(" blur ")")
167 | :backdrop-filter backdrop-filter-rule}))}
168 |
169 |
170 | {:id :backdrop-brightness
171 | :since-version [:tw 3]
172 | :rules "
173 | backdrop-brightness = <'backdrop-brightness-'> (number | percentage | fraction)
174 | "
175 | :garden (fn [{[value] :component-data}]
176 | {:--gi-backdrop-brightness (str "brightness("
177 | (value-unit->css value {:number {:value-fn div-100}})
178 | ")")
179 | :backdrop-filter backdrop-filter-rule})}
180 |
181 |
182 | {:id :backdrop-contrast
183 | :since-version [:tw 3]
184 | :rules "
185 | backdrop-contrast = <'backdrop-contrast-'> (number | percentage | fraction)
186 | "
187 | :garden (fn [{[value] :component-data}]
188 | {:--gi-backdrop-contrast (str "contrast("
189 | (value-unit->css value {:number {:value-fn div-100}})
190 | ")")
191 | :backdrop-filter backdrop-filter-rule})}
192 |
193 |
194 | {:id :backdrop-grayscale
195 | :since-version [:tw 3]
196 | :rules "
197 | backdrop-grayscale = <'backdrop-grayscale'> (<'-'> (number | percentage | fraction))?
198 | "
199 | :garden (fn [{[value] :component-data}]
200 | {:--gi-backdrop-grayscale (str "grayscale("
201 | (if (nil? value)
202 | "100%"
203 | (value-unit->css value {:number {:value-fn div-100}}))
204 | ")")
205 | :backdrop-filter backdrop-filter-rule})}
206 |
207 |
208 | {:id :backdrop-hue-rotate
209 | :since-version [:tw 3]
210 | :rules "
211 | backdrop-hue-rotate = <'backdrop-hue-rotate-'> (number | angle)
212 | "
213 | :garden (fn [{[value] :component-data}]
214 | {:--gi-backdrop-hue-rotate (str "hue-rotate("
215 | (value-unit->css value {:number {:unit "deg"}})
216 | ")")
217 | :backdrop-filter backdrop-filter-rule})}
218 |
219 |
220 | {:id :backdrop-invert
221 | :since-version [:tw 3]
222 | :rules "
223 | backdrop-invert = <'backdrop-invert'> (<'-'> (number | percentage | fraction))?
224 | "
225 | :garden (fn [{[value] :component-data}]
226 | {:--gi-backdrop-invert (str "invert("
227 | (if (nil? value)
228 | "100%"
229 | (value-unit->css value {:number {:value-fn div-100}}))
230 | ")")
231 | :backdrop-filter backdrop-filter-rule})}
232 |
233 |
234 | {:id :backdrop-opacity
235 | :since-version [:tw 3]
236 | :rules "
237 | backdrop-opacity = <'backdrop-opacity-'> (number | percentage | fraction)
238 | "
239 | :garden (fn [{[value] :component-data}]
240 | {:--gi-backdrop-opacity (str "opacity("
241 | (value-unit->css value {:number {:value-fn div-100}})
242 | ")")
243 | :backdrop-filter backdrop-filter-rule})}
244 |
245 |
246 | {:id :backdrop-saturate
247 | :since-version [:tw 3]
248 | :rules "
249 | backdrop-saturate = <'backdrop-saturate-'> (number | percentage | fraction)
250 | "
251 | :garden (fn [{[value] :component-data}]
252 | {:--gi-backdrop-saturate (str "saturate("
253 | (value-unit->css value {:number {:value-fn div-100}})
254 | ")")
255 | :backdrop-filter backdrop-filter-rule})}
256 |
257 |
258 | {:id :backdrop-sepia
259 | :since-version [:tw 3]
260 | :rules "
261 | backdrop-sepia = <'backdrop-sepia'> (<'-'> (number | percentage | fraction))?
262 | "
263 | :garden (fn [{[value] :component-data}]
264 | {:--gi-backdrop-sepia (str "sepia("
265 | (value-unit->css value {:number {:value-fn div-100}})
266 | ")")
267 | :backdrop-filter backdrop-filter-rule})}])
268 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/flexbox.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.flexbox
2 | (:require [girouette.tw.common :refer [value-unit->css div-4 mul-100]]))
3 |
4 | (def components
5 | [{:id :flex-grow
6 | :since-version [:tw 2]
7 | :rules "
8 | flex-grow = <'flex-'>? <'grow'> (<'-'> flex-grow-value)?
9 | flex-grow-value = number | fraction
10 | "
11 | :garden (fn [{data :component-data}]
12 | {:flex-grow (let [{:keys [flex-grow-value]} (into {} data)]
13 | (if (nil? flex-grow-value)
14 | 1
15 | (value-unit->css flex-grow-value)))})}
16 |
17 |
18 | {:id :flex-shrink
19 | :since-version [:tw 2]
20 | :rules "
21 | flex-shrink = <'flex-'>? <'shrink'> (<'-'> flex-shrink-value)?
22 | flex-shrink-value = number | fraction
23 | "
24 | :garden (fn [{data :component-data}]
25 | {:flex-shrink (let [{:keys [flex-shrink-value]} (into {} data)]
26 | (if (nil? flex-shrink-value)
27 | 1
28 | (value-unit->css flex-shrink-value)))})}
29 |
30 |
31 | {:id :flex-basis
32 | :since-version [:tw 2]
33 | :rules "
34 | flex-basis = <'flex-'>? <'basis'> (<'-'> flex-basis-value)?
35 | flex-basis-value = number | length | length-unit | fraction | percentage | full-100% | auto
36 | "
37 | :garden (fn [{data :component-data
38 | :keys [unitless-length-conversion]}]
39 | {:flex-basis (let [{:keys [flex-basis-value]} (into {} data)]
40 | (if (nil? flex-basis-value)
41 | 1
42 | (value-unit->css flex-basis-value {:zero-unit "px"
43 | :number unitless-length-conversion
44 | :fraction {:unit "%"
45 | :value-fn mul-100}})))})}
46 |
47 |
48 | {:id :flex-shorthand
49 | :since-version [:tw 2]
50 | :rules "
51 | flex-shorthand = <'flex-'> (flex-shorthand-1-arg | flex-shorthand-2-args | flex-shorthand-3-args)
52 | flex-shorthand-1-arg = number | fraction | 'auto' | 'initial' | 'none'
53 | flex-shorthand-2-args = flex-grow-value <'-'> (flex-shrink-value | flex-basis-value)
54 | flex-shorthand-3-args = flex-grow-value <'-'> flex-shrink-value <'-'> flex-basis-value
55 | "
56 | :garden (fn [{[[shorthand-type & args]] :component-data
57 | :keys [unitless-length-conversion]}]
58 | {:flex (case shorthand-type
59 | :flex-shorthand-1-arg
60 | (case (first args)
61 | "none" "none"
62 | "initial" "0 1 auto"
63 | "auto" "1 1 auto"
64 | (let [size (value-unit->css (first args))]
65 | (str size " " size " 0%")))
66 |
67 | :flex-shorthand-2-args
68 | (let [[[_ grow-data] [_ shrink-basis-data]] args
69 | grow-value (value-unit->css grow-data)
70 | shrink-basis-value (value-unit->css shrink-basis-data)]
71 | (str grow-value " " shrink-basis-value))
72 |
73 | :flex-shorthand-3-args
74 | (let [[[_ grow-data] [_ shrink-data] [_ basis-data]] args
75 | grow-value (value-unit->css grow-data)
76 | shrink-value (value-unit->css shrink-data)
77 | basis-value (value-unit->css basis-data {:zero-unit nil
78 | :number unitless-length-conversion
79 | :fraction {:unit "%"
80 | :value-fn mul-100}})]
81 | (str grow-value " " shrink-value " " basis-value)))})}
82 |
83 |
84 | {:id :flex-direction
85 | :since-version [:tw 2]
86 | :rules "
87 | flex-direction = <'flex-'> ('row' | 'row-reverse' | 'col' | 'col-reverse')
88 | "
89 | :garden (fn [{[direction] :component-data}]
90 | {:flex-direction ({"row" "row"
91 | "row-reverse" "row-reverse"
92 | "col" "column"
93 | "col-reverse" "column-reverse"} direction)})}
94 |
95 |
96 | {:id :flex-wrap
97 | :since-version [:tw 2]
98 | :rules "
99 | flex-wrap = <'flex-'> ('wrap' | 'wrap-reverse' | 'nowrap')
100 | "
101 | :garden (fn [{[wrap] :component-data}]
102 | {:flex-wrap wrap})}
103 |
104 |
105 | {:id :order
106 | :since-version [:tw 2]
107 | :rules "
108 | order = signus? <'order-'> order-param
109 | order-param = integer | 'first' | 'last' | 'none'
110 | "
111 | :garden (fn [props]
112 | (let [{:keys [signus order-param]} (into {} (:component-data props))]
113 | {:order (case order-param
114 | "first" -9999
115 | "last" 9999
116 | "none" 0
117 | (value-unit->css order-param {:signus signus}))}))}])
118 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/grid.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.grid
2 | (:require [girouette.tw.common :refer [value-unit->css div-4]]))
3 |
4 | (def components
5 | [{:id :grid-template-columns
6 | :since-version [:tw 2]
7 | :rules "
8 | grid-template-columns = <'grid-cols-'> (integer | none)
9 | "
10 | :garden (fn [{[repeat] :component-data}]
11 | (let [repeat (value-unit->css repeat)]
12 | {:grid-template-columns (if (#{"none" 0} repeat)
13 | "none"
14 | (str "repeat(" repeat ", minmax(0, 1fr))"))}))}
15 |
16 |
17 | {:id :grid-column-auto
18 | :since-version [:tw 2]
19 | :rules "
20 | grid-column-auto = 'col-auto'
21 | "
22 | :garden (fn [props]
23 | {:grid-column "auto"})}
24 |
25 |
26 | {:id :grid-column-span
27 | :since-version [:tw 2]
28 | :rules "
29 | grid-column-span = <'col-span-'> (integer | full)
30 | "
31 | :garden (fn [{[param] :component-data}]
32 | (let [param (value-unit->css param)]
33 | {:grid-column (if (= param "full")
34 | "-1 / 1"
35 | ;; TODO: do we need to repeat the span twice?
36 | (str "span " param " / span " param))}))}
37 |
38 |
39 | {:id :grid-column-start
40 | :since-version [:tw 2]
41 | :rules "
42 | grid-column-start = <'col-start-'> (integer | auto)
43 | "
44 | :garden (fn [{[param] :component-data}]
45 | {:grid-column-start (value-unit->css param)})}
46 |
47 |
48 | {:id :grid-column-end
49 | :since-version [:tw 2]
50 | :rules "
51 | grid-column-end = <'col-end-'> (integer | auto)
52 | "
53 | :garden (fn [{[param] :component-data}]
54 | {:grid-column-end (value-unit->css param)})}
55 |
56 |
57 | {:id :grid-template-rows
58 | :since-version [:tw 2]
59 | :rules "
60 | grid-template-rows = <'grid-rows-'> (integer | none)
61 | "
62 | :garden (fn [{[repeat] :component-data}]
63 | (let [repeat (value-unit->css repeat)]
64 | {:grid-template-rows (if (#{"none" 0} repeat)
65 | "none"
66 | (str "repeat(" repeat ", minmax(0, 1fr))"))}))}
67 |
68 |
69 | {:id :grid-row-auto
70 | :since-version [:tw 2]
71 | :rules "
72 | grid-row-auto = 'row-auto'
73 | "
74 | :garden (fn [props]
75 | {:grid-row "auto"})}
76 |
77 |
78 | {:id :grid-row-span
79 | :since-version [:tw 2]
80 | :rules "
81 | grid-row-span = <'row-span-'> (integer | full)
82 | "
83 | :garden (fn [{[param] :component-data}]
84 | (let [param (value-unit->css param)]
85 | {:grid-row (if (= param "full")
86 | "-1 / 1"
87 | ;; TODO: do we need to repeat the span twice?
88 | (str "span " param " / span " param))}))}
89 |
90 |
91 | {:id :grid-row-start
92 | :since-version [:tw 2]
93 | :rules "
94 | grid-row-start = <'row-start-'> (integer | auto)
95 | "
96 | :garden (fn [{[param] :component-data}]
97 | {:grid-row-start (value-unit->css param)})}
98 |
99 |
100 | {:id :grid-row-end
101 | :since-version [:tw 2]
102 | :rules "
103 | grid-row-end = <'row-end-'> (integer | auto)
104 | "
105 | :garden (fn [{[param] :component-data}]
106 | {:grid-row-end (value-unit->css param)})}
107 |
108 |
109 | {:id :grid-auto-flow
110 | :since-version [:tw 2]
111 | :rules "
112 | grid-auto-flow = <'grid-flow-'> ('row' | 'col' | 'row-dense' | 'col-dense')
113 | "
114 | :garden (fn [{[param] :component-data}]
115 | {:grid-auto-flow ({"row" "row"
116 | "col" "column"
117 | "row-dense" "row dense"
118 | "col-dense" "column dense"} param)})}
119 |
120 |
121 | {:id :grid-auto-columns
122 | :since-version [:tw 2]
123 | :rules "
124 | grid-auto-columns = <'auto-cols-'> ('auto' | 'min' | 'max' | 'fr')
125 | "
126 | :garden (fn [{[param] :component-data}]
127 | {:grid-auto-columns ({"auto" "auto"
128 | "min" "min-content"
129 | "max" "max-content"
130 | "fr" "minmax(0, 1fr)"} param)})}
131 |
132 |
133 | {:id :grid-auto-rows
134 | :since-version [:tw 2]
135 | :rules "
136 | grid-auto-rows = <'auto-rows-'> ('auto' | 'min' | 'max' | 'fr')
137 | "
138 | :garden (fn [{[param] :component-data}]
139 | {:grid-auto-rows ({"auto" "auto"
140 | "min" "min-content"
141 | "max" "max-content"
142 | "fr" "minmax(0, 1fr)"} param)})}
143 |
144 |
145 | {:id :gap
146 | :since-version [:tw 2]
147 | :rules "
148 | gap = <'gap-'> (axis <'-'>)? gap-value
149 | gap-value = number | length | length-unit | percentage
150 | "
151 | :garden (fn [{data :component-data
152 | :keys [unitless-length-conversion]}]
153 | (let [{:keys [axis gap-value]} (into {} data)]
154 | {({"x" :column-gap
155 | "y" :row-gap
156 | nil :gap} axis)
157 | (value-unit->css gap-value {:zero-unit nil
158 | :number unitless-length-conversion})}))}])
159 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/interactivity.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.interactivity
2 | (:require
3 | [girouette.tw.color :refer [color->css]]
4 | [girouette.tw.common :refer [value-unit->css div-4 mul-100]]))
5 |
6 | (def components
7 | [{:id :accent-color
8 | :since-version [:tw 3]
9 | :rules "
10 | accent-color = <'accent-'> ('inherit' | 'auto' | color)
11 | "
12 | :garden (fn [{[color] :component-data read-color :read-color}]
13 | {:accent-color (case color
14 | "inherit" "inherit"
15 | "auto" "auto"
16 | (color->css (read-color color)))})}
17 |
18 | {:id :appearance
19 | :since-version [:tw 2]
20 | :rules "
21 | appearance = 'appearance-none'
22 | "
23 | :garden (fn [_]
24 | {:appearance "none"})}
25 |
26 |
27 | {:id :cursor
28 | :since-version [:tw 2]
29 | :rules "
30 | cursor = <'cursor-'> ('auto' | 'default' | 'pointer' | 'wait' |
31 | 'text' | 'move' | 'help' | 'not-allowed' |
32 | 'none' | 'context-menu' | 'progress' | 'cell' |
33 | 'crosshair' | 'vertical-text' | 'alias' | 'copy' |
34 | 'no-drop' | 'grab' | 'grabbing' | 'all-scroll' |
35 | 'col-resize' | 'row-resize' |
36 | 'n-resize' | 'e-resize' | 's-resize' | 'w-resize' |
37 | 'ne-resize' | 'nw-resize' | 'se-resize' | 'sw-resize' |
38 | 'nesw-resize' | 'nwse-resize' |
39 | 'zoom-in' | 'zoom-out')
40 | "
41 | :garden (fn [{[type] :component-data}]
42 | {:cursor type})}
43 |
44 |
45 | {:id :outline
46 | :since-version [:tw 2]
47 | :removed-in-version [:tw 3]
48 | :rules "
49 | outline = <'outline-'> ('none' | 'white' | 'black')
50 | "
51 | :garden (fn [{[type] :component-data}]
52 | {:outline ({"none" "2px solid transparent"
53 | "white" "2px dotted white"
54 | "black" "2px dotted black"} type)
55 | :outline-offset "2px"})}
56 |
57 |
58 | {:id :pointer-events
59 | :since-version [:tw 2]
60 | :rules "
61 | pointer-events = <'pointer-events-'> ('none' | 'auto')
62 | "
63 | :garden (fn [{[type] :component-data}]
64 | {:pointer-events type})}
65 |
66 |
67 | {:id :resize
68 | :since-version [:tw 2]
69 | :rules "
70 | resize = <'resize'> (<'-'> ('none' | 'x' | 'y'))?
71 | "
72 | :garden (fn [{[type] :component-data}]
73 | {:resize ({"none" "none"
74 | "x" "horizontal"
75 | "y" "vertical"
76 | nil "both"} type)})}
77 |
78 |
79 | {:id :scroll-behavior
80 | :since-version [:tw 3]
81 | :rules "
82 | scroll-behavior = <'scroll-'> ('auto' | 'smooth')
83 | "
84 | :garden (fn [{[value] :component-data}]
85 | {:scroll-behavior value})}
86 |
87 |
88 | {:id :scroll-margin
89 | :since-version [:tw 3]
90 | :rules "
91 | scroll-margin = signus? <'scroll-m'> (direction | axis)? <'-'> scroll-margin-value
92 | scroll-margin-value = number | length | length-unit
93 | "
94 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
95 | (let [{:keys [signus direction axis scroll-margin-value]} (into {} component-data)
96 | directions (case direction
97 | "t" ["top"]
98 | "r" ["right"]
99 | "b" ["bottom"]
100 | "l" ["left"]
101 | (case axis
102 | "x" ["left" "right"]
103 | "y" ["top" "bottom"]
104 | nil))
105 | props (if (nil? directions)
106 | [:scroll-margin]
107 | (mapv (fn [dir]
108 | (keyword (str "scroll-margin-" dir)))
109 | directions))
110 | value-css (value-unit->css scroll-margin-value
111 | {:signus signus
112 | :zero-unit "px"
113 | :number unitless-length-conversion})]
114 | (into {}
115 | (map (fn [prop]
116 | [prop value-css]))
117 | props)))}
118 |
119 |
120 | {:id :scroll-padding
121 | :since-version [:tw 3]
122 | :rules "
123 | scroll-padding = signus? <'scroll-p'> (direction | axis)? <'-'> scroll-padding-value
124 | scroll-padding-value = number | length | length-unit | fraction | percentage
125 | "
126 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
127 | (let [{:keys [signus direction axis scroll-padding-value]}
128 | (into {} component-data)
129 | directions (case direction
130 | "t" ["top"]
131 | "r" ["right"]
132 | "b" ["bottom"]
133 | "l" ["left"]
134 | (case axis
135 | "x" ["left" "right"]
136 | "y" ["top" "bottom"]
137 | nil))
138 | props (if (nil? directions)
139 | [:scroll-padding]
140 | (mapv (fn [dir]
141 | (keyword (str "scroll-padding-" dir)))
142 | directions))
143 | value-css (value-unit->css scroll-padding-value
144 | {:signus signus
145 | :zero-unit "px"
146 | :number unitless-length-conversion
147 | :fraction {:unit "%"
148 | :value-fn mul-100}})]
149 | (into {}
150 | (map (fn [prop]
151 | [prop value-css]))
152 | props)))}
153 |
154 |
155 | {:id :scroll-snap-align
156 | :since-version [:tw 3]
157 | :rules "
158 | scroll-snap-align = <'snap-'> ('start' | 'end' | 'center' | (<'align-'> 'none'))
159 | "
160 | :garden (fn [{[value] :component-data}]
161 | {:scroll-snap-align value})}
162 |
163 |
164 | {:id :scroll-snap-stop
165 | :since-version [:tw 3]
166 | :rules "
167 | scroll-snap-stop = <'snap-'> ('normal' | 'always')
168 | "
169 | :garden (fn [{[value] :component-data}]
170 | {:scroll-snap-stop value})}
171 |
172 |
173 | {:id :scroll-snap-type
174 | :since-version [:tw 3]
175 | :rules "
176 | scroll-snap-type = <'snap-'> (scroll-snap-type-dir | scroll-snap-type-strictness)
177 | scroll-snap-type-dir = 'none' | 'x' | 'y' | 'both'
178 | scroll-snap-type-strictness = 'mandatory' | 'proximity'
179 | "
180 | :garden (fn [{:keys [component-data]}]
181 | (let [{:keys [scroll-snap-type-dir scroll-snap-type-strictness]} (into {} component-data)]
182 | (cond
183 | (some? scroll-snap-type-strictness)
184 | {:--gi-scroll-snap-strictness scroll-snap-type-strictness}
185 |
186 | (= scroll-snap-type-dir "none")
187 | {:scroll-snap-type "none"}
188 |
189 | :else
190 | {:scroll-snap-type (str scroll-snap-type-dir " var(--gi-scroll-snap-strictness)")})))}
191 |
192 |
193 | {:id :touch-action
194 | :since-version [:tw 3]
195 | :rules "
196 | touch-action = <'touch-'> ('auto' | 'none' |
197 | 'pan-x' | 'pan-y' |
198 | 'pan-up' | 'pan-right' | 'pan-down' |'pan-left' |
199 | 'pinch-zoom' | 'manipulation')
200 | "
201 | :garden (fn [{[value] :component-data}]
202 | {:touch-action value})}
203 |
204 |
205 | {:id :user-select
206 | :since-version [:tw 2]
207 | :rules "
208 | user-select = <'select-'> ('none' | 'text' | 'all' | 'auto')
209 | "
210 | :garden (fn [{[selection-type] :component-data}]
211 | {:user-select selection-type})}
212 |
213 |
214 | {:id :will-change
215 | :since-version [:tw 3]
216 | :rules "
217 | will-change = <'will-change-'> ('auto' | 'scroll' | 'contents' | 'transform')
218 | "
219 | :garden (fn [{[value] :component-data}]
220 | {:will-change ({"auto" "auto"
221 | "scroll" "scroll-position"
222 | "contents" "contents"
223 | "transform" "transform"} value)})}])
224 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/layout.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.layout
2 | (:require [clojure.string :as str]
3 | [girouette.tw.common :refer [value-unit->css breakpoint->pixels div-4 mul-100 ratio-str]]))
4 |
5 | (def components
6 | [{:id :aspect-ratio
7 | :since-version [:tw 3]
8 | :rules "
9 | aspect-ratio = <'aspect-'> (aspect-ratio-fixed | aspect-ratio-as-ratio)
10 | aspect-ratio-fixed = 'auto' | 'square' | 'video'
11 | aspect-ratio-as-ratio = ratio
12 | "
13 | :garden (fn [{:keys [component-data]}]
14 | (let [{:keys [aspect-ratio-fixed aspect-ratio-as-ratio]} (into {} component-data)]
15 | {:aspect-ratio (if (some? aspect-ratio-fixed)
16 | (case aspect-ratio-fixed
17 | "auto" "auto"
18 | "square" "1 / 1"
19 | "video" "16 / 9")
20 | (value-unit->css aspect-ratio-as-ratio {:ratio {:value-fn ratio-str}}))}))}
21 |
22 |
23 | {:id :container
24 | :since-version [:tw 2]
25 | :rules "
26 | container = <'container'>
27 | "
28 | :garden (fn [props]
29 | (if-some [media-query-min-width (-> props :prefixes :media-query-min-width)]
30 | {:max-width (breakpoint->pixels media-query-min-width)}
31 | {:width "100%"}))}
32 |
33 |
34 | {:id :columns
35 | :since-version [:tw 3]
36 | :rules "
37 | columns = <'columns-'> (columns-count | columns-width | columns-count <'-'> columns-width)
38 | columns-count = integer | 'auto'
39 | columns-width = '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' |
40 | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl' |
41 | length | 'auto'
42 | "
43 | :garden (fn [{:keys [component-data]}]
44 | (let [{:keys [columns-count columns-width]} (into {} component-data)]
45 | {:columns (->> [(when (some? columns-count)
46 | (if (= columns-count "auto")
47 | "auto"
48 | (value-unit->css columns-count)))
49 | (when (some? columns-width)
50 | (case columns-width
51 | "3xs" "16rem"
52 | "2xs" "18rem"
53 | "xs" "20rem"
54 | "sm" "24rem"
55 | "md" "28rem"
56 | "lg" "32rem"
57 | "xl" "36rem"
58 | "2xl" "42rem"
59 | "3xl" "48rem"
60 | "4xl" "56rem"
61 | "5xl" "64rem"
62 | "6xl" "72rem"
63 | "7xl" "80rem"
64 | "auto" "auto"
65 | (value-unit->css columns-width)))]
66 | (remove nil?)
67 | (str/join " "))}))}
68 |
69 |
70 | {:id :break-after
71 | :since-version [:tw 3]
72 | :rules "
73 | break-after = <'break-after-'> ('auto' | 'avoid' | 'all' | 'avoid-page' |
74 | 'page' | 'left' | 'right' | 'column')
75 | "
76 | :garden (fn [{[value] :component-data}]
77 | {:break-after value})}
78 |
79 |
80 | {:id :break-before
81 | :since-version [:tw 3]
82 | :rules "
83 | break-before = <'break-before-'> ('auto' | 'avoid' | 'all' | 'avoid-page' |
84 | 'page' | 'left' | 'right' | 'column')
85 | "
86 | :garden (fn [{[value] :component-data}]
87 | {:break-before value})}
88 |
89 |
90 | {:id :break-inside
91 | :since-version [:tw 3]
92 | :rules "
93 | break-inside = <'break-inside-'> ('auto' | 'avoid' | 'avoid-page' | 'avoid-column')
94 | "
95 | :garden (fn [{[value] :component-data}]
96 | {:break-inside value})}
97 |
98 |
99 | {:id :box-decoration-break
100 | :since-version [:tw 3]
101 | :rules "
102 | box-decoration-break = <'box-decoration-'> ('clone' | 'slice')
103 | "
104 | :garden (fn [{[decoration-break] :component-data}]
105 | {:box-decoration-break decoration-break})}
106 |
107 |
108 | {:id :box-sizing
109 | :since-version [:tw 2]
110 | :rules "
111 | box-sizing = <'box-'> ('border' | 'content')
112 | "
113 | :garden (fn [{[box-model] :component-data}]
114 | {:box-sizing (case box-model
115 | "border" "border-box"
116 | "content" "content-box")})}
117 |
118 |
119 | {:id :display
120 | :since-version [:tw 2]
121 | :rules "
122 | display = 'block' | 'inline-block' | 'inline' | 'flex' | 'inline-flex' |
123 | 'table' | 'inline-table' | 'table-caption' | 'table-cell' | 'table-column' |
124 | 'table-column-group' | 'table-footer-group' | 'table-header-group' | 'table-row-group' |
125 | 'table-row' | 'flow-root' | 'grid' |'inline-grid' | 'contents' | 'list-item' | 'hidden'
126 | "
127 | :garden (fn [{[display-mode] :component-data}]
128 | {:display (if (= "hidden" display-mode)
129 | "none"
130 | display-mode)})}
131 |
132 |
133 | {:id :floats
134 | :since-version [:tw 2]
135 | :rules "
136 | floats = <'float-'> ('left' | 'right' | 'none')
137 | "
138 | :garden (fn [{[direction] :component-data}]
139 | {:float direction})}
140 |
141 |
142 | {:id :clear
143 | :since-version [:tw 2]
144 | :rules "
145 | clear = <'clear-'> ('left' | 'right' | 'both' | 'none')
146 | "
147 | :garden (fn [{[direction] :component-data}]
148 | {:clear direction})}
149 |
150 | {:id :isolation
151 | :since-version [:tw 3]
152 | :rules "
153 | isolation = 'isolate' | <'isolation-'> 'auto'
154 | "
155 | :garden (fn [{[isolation] :component-data}]
156 | {:isolation isolation})}
157 |
158 | {:id :object-fit
159 | :since-version [:tw 2]
160 | :rules "
161 | object-fit = <'object-'> ('contain' | 'cover' | 'fill' | 'none' | 'scale-down')
162 | "
163 | :garden (fn [{[fitness] :component-data}]
164 | {:object-fit fitness})}
165 |
166 |
167 | {:id :object-position
168 | :since-version [:tw 2]
169 | :rules "
170 | object-position = <'object-'> object-position-side
171 | = 'left' | 'left-bottom' | 'left-top' |
172 | 'right' | 'right-bottom' | 'right-top' |
173 | 'center' | 'bottom' | 'top'
174 | "
175 | :garden (fn [{[position] :component-data}]
176 | {:object-position (str/escape position {\- \space})})}
177 |
178 |
179 | {:id :overflow
180 | :since-version [:tw 2]
181 | :rules "
182 | overflow = <'overflow-'> (axis <'-'>)? overflow-mode
183 | overflow-mode = 'auto' | 'hidden' | 'clip' | 'visible' | 'scroll'
184 | "
185 | :garden (fn [{:keys [component-data]}]
186 | (let [{:keys [axis overflow-mode]} (into {} component-data)
187 | property (str "overflow" (when axis (str "-" axis)))]
188 | {(keyword property) overflow-mode}))}
189 |
190 |
191 | {:id :overscroll
192 | :since-version [:tw 2]
193 | :rules "
194 | overscroll = <'overscroll-'> (axis <'-'>)? overscroll-mode
195 | overscroll-mode = 'auto' | 'contain' | 'none'
196 | "
197 | :garden (fn [{:keys [component-data]}]
198 | (let [{:keys [axis overscroll-mode]} (into {} component-data)
199 | property (str "overscroll" (when axis (str "-" axis)))]
200 | {(keyword property) overscroll-mode}))}
201 |
202 |
203 | {:id :position
204 | :since-version [:tw 2]
205 | :rules "
206 | position = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky'
207 | "
208 | :garden (fn [{[position] :component-data}]
209 | {:position position})}
210 |
211 |
212 | {:id :positioning
213 | :since-version [:tw 2]
214 | :rules "
215 | positioning = signus? positioning-mode <'-'> positioning-value
216 | positioning-mode = 'top' | 'right' | 'bottom' | 'left' | #'inset(-x|-y)?'
217 | positioning-value = number | length | length-unit | fraction | percentage | full-100% | auto
218 | "
219 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
220 | (let [{:keys [signus positioning-mode positioning-value]} (into {} component-data)
221 | directions ({"inset" [:top :right :bottom :left]
222 | "inset-x" [:right :left]
223 | "inset-y" [:top :bottom]
224 | "top" [:top]
225 | "right" [:right]
226 | "bottom" [:bottom]
227 | "left" [:left]} positioning-mode)
228 | value-css (value-unit->css positioning-value
229 | {:signus signus
230 | :zero-unit nil
231 | :number unitless-length-conversion
232 | :fraction {:unit "%"
233 | :value-fn mul-100}})]
234 | (into {}
235 | (map (fn [direction] [direction value-css]))
236 | directions)))}
237 |
238 |
239 | {:id :visibility
240 | :since-version [:tw 2]
241 | :rules "
242 | visibility = 'visible' | 'invisible'
243 | "
244 | :garden (fn [{[visibility] :component-data}]
245 | {:visibility ({"visible" "visible"
246 | "invisible" "hidden"} visibility)})}
247 |
248 |
249 | {:id :z-index
250 | :since-version [:tw 2]
251 | :rules "
252 | z-index = <'z-'> (integer | auto)
253 | "
254 | :garden (fn [{[index] :component-data}]
255 | {:z-index (value-unit->css index)})}])
256 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/sizing.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.sizing
2 | (:require [girouette.tw.common :refer [value-unit->css div-4 mul-100]]))
3 |
4 | (def components
5 | [{:id :width
6 | :since-version [:tw 2]
7 | :rules "
8 | width = <'w-'> (number | length | length-unit | fraction | percentage | full-100% |
9 | auto | screen-100vw | min-content | max-content | fit-content)
10 | "
11 | :garden (fn [{[value-data] :component-data
12 | :keys [unitless-length-conversion]}]
13 | {:width (value-unit->css value-data
14 | {:zero-unit nil
15 | :number unitless-length-conversion
16 | :fraction {:unit "%"
17 | :value-fn mul-100}})})}
18 |
19 |
20 | {:id :min-width
21 | :since-version [:tw 2]
22 | :rules "
23 | min-width = <'min-w-'> (number | length | length-unit | fraction | percentage | full-100% |
24 | auto | screen-100vw | min-content | max-content | fit-content)
25 | "
26 | :garden (fn [{[value-data] :component-data
27 | :keys [unitless-length-conversion]}]
28 | {:min-width (value-unit->css value-data
29 | {:zero-unit nil
30 | :number unitless-length-conversion
31 | :fraction {:unit "%"
32 | :value-fn mul-100}})})}
33 |
34 |
35 | {:id :max-width
36 | :since-version [:tw 2]
37 | :rules "
38 | max-width = <'max-w-'> (max-width-fixed-size | max-width-generic-size)
39 | max-width-fixed-size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl' |
40 | 'prose' | 'screen-sm' | 'screen-md' | 'screen-lg' | 'screen-xl' | 'screen-2xl'
41 | max-width-generic-size = number | length | length-unit | fraction | percentage | full-100% |
42 | none | screen-100vw | min-content | max-content | fit-content
43 | "
44 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
45 | (let [{:keys [max-width-fixed-size max-width-generic-size]} (into {} component-data)]
46 | {:max-width (if (some? max-width-fixed-size)
47 | ({"xs" "20rem"
48 | "sm" "24rem"
49 | "md" "28rem"
50 | "lg" "32rem"
51 | "xl" "36rem"
52 | "2xl" "42rem"
53 | "3xl" "48rem"
54 | "4xl" "56rem"
55 | "5xl" "64rem"
56 | "6xl" "72rem"
57 | "7xl" "80rem"
58 | "prose" "65ch"
59 | "screen-sm" "640px"
60 | "screen-md" "768px"
61 | "screen-lg" "1024px"
62 | "screen-xl" "1280px"
63 | "screen-2xl" "1536px"} max-width-fixed-size)
64 | (value-unit->css max-width-generic-size
65 | {:zero-unit nil
66 | :number unitless-length-conversion
67 | :fraction {:unit "%"
68 | :value-fn mul-100}}))}))}
69 |
70 |
71 | {:id :height
72 | :since-version [:tw 2]
73 | :rules "
74 | height = <'h-'> (number | length | length-unit | fraction | percentage | full-100% |
75 | auto | screen-100vh | min-content | max-content | fit-content)
76 | "
77 | :garden (fn [{[value-data] :component-data
78 | :keys [unitless-length-conversion]}]
79 | {:height (value-unit->css value-data
80 | {:zero-unit nil
81 | :number unitless-length-conversion
82 | :fraction {:unit "%"
83 | :value-fn mul-100}})})}
84 |
85 |
86 | {:id :min-height
87 | :since-version [:tw 2]
88 | :rules "
89 | min-height = <'min-h-'> (number | length | length-unit | fraction | percentage | full-100% |
90 | auto | screen-100vh | min-content | max-content | fit-content)
91 | "
92 | :garden (fn [{[value-data] :component-data
93 | :keys [unitless-length-conversion]}]
94 | {:min-height (value-unit->css value-data
95 | {:zero-unit nil
96 | :number unitless-length-conversion
97 | :fraction {:unit "%"
98 | :value-fn mul-100}})})}
99 |
100 |
101 | {:id :max-height
102 | :since-version [:tw 2]
103 | :rules "
104 | max-height = <'max-h-'> (number | length | length-unit | fraction | percentage | full-100% |
105 | none | screen-100vh | min-content | max-content | fit-content)
106 | "
107 | :garden (fn [{[value-data] :component-data
108 | :keys [unitless-length-conversion]}]
109 | {:max-height (value-unit->css value-data
110 | {:zero-unit nil
111 | :number unitless-length-conversion
112 | :fraction {:unit "%"
113 | :value-fn mul-100}})})}])
114 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/spacing.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.spacing
2 | (:require [girouette.tw.common :refer [value-unit->css div-4 between-children-selector]]))
3 |
4 |
5 | (def components
6 | [{:id :padding
7 | :since-version [:tw 2]
8 | :rules "
9 | padding = signus? <'p'> (direction | axis)? <'-'> padding-value
10 | padding-value = number | length | length-unit
11 | "
12 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
13 | (let [{:keys [signus direction axis padding-value]} (into {} component-data)
14 | directions (case direction
15 | "t" ["top"]
16 | "r" ["right"]
17 | "b" ["bottom"]
18 | "l" ["left"]
19 | (case axis
20 | "x" ["left" "right"]
21 | "y" ["top" "bottom"]
22 | nil))
23 | value-css (value-unit->css padding-value {:signus signus
24 | :zero-unit nil
25 | :number unitless-length-conversion})]
26 | (if (nil? directions)
27 | {:padding value-css}
28 | (into {}
29 | (map (fn [direction] [(keyword (str "padding-" direction)) value-css]))
30 | directions))))}
31 |
32 |
33 | {:id :margin
34 | :since-version [:tw 2]
35 | :rules "
36 | margin = signus? <'m'> (direction | axis)? <'-'> margin-value
37 | margin-value = number | length | length-unit | auto
38 | "
39 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
40 | (let [{:keys [signus direction axis margin-value]} (into {} component-data)
41 | directions (case direction
42 | "t" ["top"]
43 | "r" ["right"]
44 | "b" ["bottom"]
45 | "l" ["left"]
46 | (case axis
47 | "x" ["left" "right"]
48 | "y" ["top" "bottom"]
49 | nil))
50 | value-css (value-unit->css margin-value {:signus signus
51 | :zero-unit nil
52 | :number unitless-length-conversion})]
53 | (if (nil? directions)
54 | {:margin value-css}
55 | (into {}
56 | (map (fn [direction] [(keyword (str "margin-" direction)) value-css]))
57 | directions))))}
58 |
59 |
60 | {:id :space-between
61 | :since-version [:tw 2]
62 | :rules "
63 | space-between = signus? <'space-'> axis <'-'> space-between-value (<'-'> space-between-reverse)?
64 | space-between-value = number | length | length-unit
65 | space-between-reverse = 'reverse'
66 | "
67 | :garden (fn [{:keys [component-data unitless-length-conversion]}]
68 | (let [{:keys [signus axis space-between-value space-between-reverse]} (into {} component-data)
69 | direction ({["x" false] "left"
70 | ["x" true] "right"
71 | ["y" false] "top"
72 | ["y" true] "bottom"} [axis (some? space-between-reverse)])
73 | value-css (value-unit->css space-between-value {:signus signus
74 | :zero-unit nil
75 | :number unitless-length-conversion})]
76 | [between-children-selector {(keyword (str "margin-" direction)) value-css}]))}])
77 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/svg.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.svg
2 | (:require [girouette.tw.color :refer [color->css]]
3 | [girouette.tw.common :refer [value-unit->css]]))
4 |
5 | (def components
6 | [{:id :fill
7 | :since-version [:tw 2]
8 | :rules "
9 | fill = <'fill-'> color
10 | "
11 | :garden (fn [{[color] :component-data
12 | read-color :read-color}]
13 | {:fill (color->css (read-color color))})}
14 |
15 |
16 | {:id :stroke
17 | :since-version [:tw 2]
18 | :rules "
19 | stroke = <'stroke-'> color
20 | "
21 | :garden (fn [{[color] :component-data
22 | read-color :read-color}]
23 | {:stroke (color->css (read-color color))})}
24 |
25 |
26 | {:id :stroke-width
27 | :since-version [:tw 2]
28 | :rules "
29 | stroke-width = <'stroke-'> number
30 | "
31 | :garden (fn [{[thickness] :component-data}]
32 | {:stroke-width (value-unit->css thickness)})}])
33 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/table.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.table)
2 |
3 | (def components
4 | [{:id :border-collapse
5 | :since-version [:tw 2]
6 | :rules "
7 | border-collapse = <'border-'> ('collapse' | 'separate')
8 | "
9 | :garden (fn [{[type] :component-data}]
10 | {:border-collapse type})}
11 |
12 |
13 | {:id :table-layout
14 | :since-version [:tw 2]
15 | :rules "
16 | table-layout = <'table-'> ('auto' | 'fixed')
17 | "
18 | :garden (fn [{[type] :component-data}]
19 | {:table-layout type})}])
20 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/tw/transform.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.tw.transform
2 | (:require [clojure.string :as str]
3 | [girouette.tw.common :refer [value-unit->css div-100 div-4 mul-100]]))
4 |
5 | (def components
6 | [{:id :transform
7 | :since-version [:tw 2]
8 | :rules "
9 | transform = 'transform' | 'transform-gpu' | 'transform-none'
10 | "
11 | :garden (fn [{[transform-type] :component-data}]
12 | (case transform-type
13 | "transform" {:--gi-translate-x 0
14 | :--gi-translate-y 0
15 | :--gi-rotate 0
16 | :--gi-skew-x 0
17 | :--gi-skew-y 0
18 | :--gi-scale-x 1
19 | :--gi-scale-y 1
20 | :transform (str "translateX(var(--gi-translate-x)) "
21 | "translateY(var(--gi-translate-y)) "
22 | "rotate(var(--gi-rotate)) "
23 | "skewX(var(--gi-skew-x)) "
24 | "skewY(var(--gi-skew-y)) "
25 | "scaleX(var(--gi-scale-x)) "
26 | "scaleY(var(--gi-scale-y))")}
27 | "transform-gpu" {:--gi-translate-x 0
28 | :--gi-translate-y 0
29 | :--gi-rotate 0
30 | :--gi-skew-x 0
31 | :--gi-skew-y 0
32 | :--gi-scale-x 1
33 | :--gi-scale-y 1
34 | :transform (str "translate3d(var(--gi-translate-x),var(--gi-translate-y),0) "
35 | "rotate(var(--gi-rotate)) "
36 | "skewX(var(--gi-skew-x)) "
37 | "skewY(var(--gi-skew-y)) "
38 | "scaleX(var(--gi-scale-x)) "
39 | "scaleY(var(--gi-scale-y))")}
40 | "transform-none" {:transform "none"}))
41 | :before-rules #{:translate :rotate :skew :scale}}
42 |
43 |
44 | {:id :transform-origin
45 | :since-version [:tw 2]
46 | :rules "
47 | transform-origin = <'origin-'> ('top-left'| 'top' | 'top-right' |
48 | 'left' | 'center' | 'right' |
49 | 'bottom-left' | 'bottom' | 'bottom-right')
50 | "
51 | :garden (fn [{[direction] :component-data}]
52 | {:transform-origin (str/escape direction {\- \space})})}
53 |
54 |
55 | {:id :scale
56 | :since-version [:tw 2]
57 | :rules "
58 | scale = signus? <'scale-'> (axis <'-'>)? scale-value
59 | scale-value = number
60 | "
61 | :garden (fn [{data :component-data}]
62 | (let [{:keys [signus axis scale-value]} (into {} data)
63 | axes ({"x" ["x"]
64 | "y" ["y"]
65 | nil ["x" "y"]} axis)
66 | value (value-unit->css scale-value {:signus signus
67 | :value-fn div-100})]
68 | (into {}
69 | (map (fn [axis]
70 | [(keyword (str "--gi-scale-" axis)) value]))
71 | axes)))}
72 |
73 |
74 | {:id :rotate
75 | :since-version [:tw 2]
76 | :rules "
77 | rotate = signus? <'rotate-'> rotate-value
78 | rotate-value = number | angle
79 | "
80 | :garden (fn [{data :component-data}]
81 | (let [{:keys [signus rotate-value]} (into {} data)]
82 | {:--gi-rotate (value-unit->css rotate-value {:signus signus
83 | :zero-unit nil
84 | :number {:unit "deg"}})}))}
85 |
86 |
87 | {:id :translate
88 | :since-version [:tw 2]
89 | :rules "
90 | translate = signus? <'translate-'> axis <'-'> translate-value
91 | translate-value = number | length | length-unit | fraction | percentage | full-100%
92 | "
93 | :garden (fn [{data :component-data
94 | :keys [unitless-length-conversion]}]
95 | (let [{:keys [signus axis translate-value]} (into {} data)
96 | attribute ({"x" :--gi-translate-x
97 | "y" :--gi-translate-y} axis)]
98 | {attribute (value-unit->css translate-value
99 | {:signus signus
100 | :zero-unit nil
101 | :number unitless-length-conversion
102 | :fraction {:unit "%"
103 | :value-fn mul-100}})}))}
104 |
105 |
106 | {:id :skew
107 | :since-version [:tw 2]
108 | :rules "
109 | skew = signus? <'skew-'> axis <'-'> skew-value
110 | skew-value = number | angle
111 | "
112 | :garden (fn [{data :component-data}]
113 | (let [{:keys [signus axis skew-value]} (into {} data)
114 | attribute ({"x" :--gi-skew-x
115 | "y" :--gi-skew-y} axis)
116 | value (value-unit->css skew-value {:signus signus
117 | :zero-unit nil
118 | :number {:unit "deg"}})]
119 | {attribute value}))}])
120 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/util.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc girouette.util
2 | #?(:cljs (:require-macros girouette.util))
3 | (:refer-clojure :exclude [group-by])
4 | (:require [clojure.core :as cc]))
5 |
6 | (defmacro implies [x y]
7 | `(or (not ~x) ~y))
8 |
9 | (defmacro comp-> [& args]
10 | `(comp ~@(reverse args)))
11 |
12 | (defn group-by
13 | "Same as clojure.core/group-by, but with some handy new arities which apply
14 | custom map & reduce operations to the elements grouped together under the same key."
15 | ([kf coll]
16 | ;(group-by kf identity conj [] coll)
17 | (cc/group-by kf coll))
18 | ([kf vf coll]
19 | (group-by kf vf conj [] coll))
20 | ([kf vf rf coll]
21 | (group-by kf vf rf (rf) coll))
22 | ([kf vf rf init coll]
23 | (->> coll
24 | (reduce (fn [ret x]
25 | (let [k (kf x)
26 | v (vf x)]
27 | (assoc! ret k (rf (get ret k init) v))))
28 | (transient {}))
29 | persistent!)))
30 |
31 | #_ (group-by first [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
32 | #_ (group-by first second [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
33 | #_ (group-by first second + [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
34 | #_ (group-by first second + 10 [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
35 |
36 | (defn index-by
37 | ([kf coll]
38 | (index-by kf identity coll))
39 | ([kf vf coll]
40 | (->> coll
41 | (reduce (fn [ret x]
42 | (assoc! ret (kf x) (vf x)))
43 | (transient {}))
44 | persistent!)))
45 |
46 | #_ (index-by first [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
47 | #_ (index-by first second [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
48 |
49 | (defn into-one-vector
50 | "Flattens the outer vectors/lists/seqs/sets in the provided data and returns one vector with the data inside."
51 | [data]
52 | (if (or (sequential? data)
53 | (set? data))
54 | (into [] (mapcat into-one-vector) data)
55 | [data]))
56 |
57 | #_ (into-one-vector 3)
58 | #_ (into-one-vector [3 4 5 6])
59 | #_ (into-one-vector [3 [4 5 6]])
60 | #_ (into-one-vector [[3] [[4] 5 [6]]])
61 | #_ (into-one-vector [[3] [] [[]] [[4] [5]] 6])
62 |
--------------------------------------------------------------------------------
/lib/girouette/src/girouette/version.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.version
2 | (:require [girouette.util :as util]))
3 |
4 | (defn- version-category-= [version1 version2]
5 | (= (first version1) (first version2)))
6 |
7 |
8 | (defn- version-< [version1 version2]
9 | (loop [;; Skips the version category, e.g. `:tw` or `:gi`
10 | v1 (next version1)
11 | v2 (next version2)]
12 | (if (or (seq v1) (seq v2))
13 | (let [x1 (or (first v1) 0)
14 | x2 (or (first v2) 0)]
15 | (cond
16 | (< x1 x2) true
17 | (> x1 x2) false
18 | :else (recur (next v1) (next v2))))
19 | false)))
20 |
21 |
22 | (defn- version-<= [version1 version2]
23 | (loop [;; Skips the version category, e.g. `:tw` or `:gi`
24 | v1 (next version1)
25 | v2 (next version2)]
26 | (if (or (seq v1) (seq v2))
27 | (let [x1 (or (first v1) 0)
28 | x2 (or (first v2) 0)]
29 | (cond
30 | (< x1 x2) true
31 | (> x1 x2) false
32 | :else (recur (next v1) (next v2))))
33 | true)))
34 |
35 |
36 | (defn filter-components-by-version
37 | "Filters the `components` according to a given version."
38 | [components version]
39 | (filter (fn [component]
40 | (let [{:keys [since-version removed-in-version]} component]
41 | (and (version-category-= since-version version)
42 | (version-<= since-version version)
43 | (util/implies (some? removed-in-version)
44 | (version-< version removed-in-version)))))
45 | (util/into-one-vector components)))
46 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/garden/util_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.garden.util-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]
4 | [girouette.tw.common :refer [dot]]
5 | [girouette.garden.util :refer [apply-class-rules]]))
6 |
7 | (deftest apply-class-rules-test
8 | (are [target-class-name gi-class-names expected-garden]
9 | (= expected-garden
10 | (apply-class-rules (dot target-class-name)
11 | (mapv tw-v3-class-name->garden gi-class-names)
12 | (mapv dot gi-class-names)))
13 |
14 | "my-class"
15 | ["p-3"
16 | "m-3"
17 | "hover:p-4"
18 | "sm:p-1"
19 | "sm:m-1"
20 | "sm:hover:p-2"]
21 | '([".my-class" {:margin "0.75rem"
22 | :padding "0.75rem"}]
23 | [".my-class" [:&:hover {:padding "1rem"}]]
24 | #garden.types.CSSAtRule {:identifier :media
25 | :value {:media-queries {:min-width "640px"}
26 | :rules [[".my-class" {:padding "0.25rem"
27 | :margin "0.25rem"}]
28 | [".my-class" [:&:hover {:padding "0.5rem"}]]]}})))
29 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/grammar/hiccup_tag_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.grammar.hiccup-tag-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.grammar.hiccup-tag :refer [hiccup-tag-parser]]))
4 |
5 | (deftest parser-test
6 | (are [kw expected-parsed-data]
7 | (= expected-parsed-data (hiccup-tag-parser (name kw)))
8 |
9 | :div
10 | [:hiccup-tag [:html-tag "div"]]
11 |
12 | :div#app.foo
13 | [:hiccup-tag [:html-tag "div"] [:id "app"] [:class-name "foo"]]
14 |
15 | :div.foo#here
16 | [:hiccup-tag [:html-tag "div"] [:class-name "foo"] [:id "here"]]
17 |
18 | :div#app.foo#here.bar
19 | [:hiccup-tag [:html-tag "div"] [:id "app"] [:class-name "foo"] [:id "here"] [:class-name "bar"]]))
20 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/accessibility_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.accessibility-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "sr-only"
10 | [".sr-only" {:position "absolute"
11 | :width "1px"
12 | :height "1px"
13 | :padding 0
14 | :margin "-1px"
15 | :overflow "hidden"
16 | :clip "rect(0,0,0,0)"
17 | :white-space "nowrap"
18 | :border-width 0}]))
19 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/animation_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.animation-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "transition"
10 | [".transition"
11 | {:transition-property "background-color,border-color,color,fill,stroke,opacity,box-shadow,transform"
12 | :transition-timing-function "cubic-bezier(0.4,0,0.2,1)"
13 | :transition-duration "150ms"}]
14 |
15 | "transition-colors"
16 | [".transition-colors"
17 | {:transition-property "background-color,border-color,color,fill,stroke"
18 | :transition-timing-function "cubic-bezier(0.4,0,0.2,1)"
19 | :transition-duration "150ms"}]
20 |
21 | "duration-0"
22 | [".duration-0" {:transition-duration "0s"}]
23 |
24 | "duration-100"
25 | [".duration-100" {:transition-duration "100ms"}]
26 |
27 | "duration-100ms"
28 | [".duration-100ms" {:transition-duration "100ms"}]
29 |
30 | "duration-2s"
31 | [".duration-2s" {:transition-duration "2s"}]
32 |
33 | "ease-linear"
34 | [".ease-linear" {:transition-timing-function "linear"}]
35 |
36 | "ease-in"
37 | [".ease-in" {:transition-timing-function "cubic-bezier(0.4,0,1,1)"}]
38 |
39 | "delay-0"
40 | [".delay-0" {:transition-delay "0s"}]
41 |
42 | "delay-100"
43 | [".delay-100" {:transition-delay "100ms"}]
44 |
45 | "delay-100ms"
46 | [".delay-100ms" {:transition-delay "100ms"}]
47 |
48 | "delay-2s"
49 | [".delay-2s" {:transition-delay "2s"}]
50 |
51 | "animate-none"
52 | [".animate-none" {:animation "none"}]
53 |
54 | "animate-spin"
55 | [".animate-spin"
56 | [{:animation "spin 1s linear infinite"}
57 | #garden.types.CSSAtRule{:identifier :keyframes
58 | :value {:identifier "spin"
59 | :frames ([:from {:transform "rotate(0)"}]
60 | [:to {:transform "rotate(360deg)"}])}}]]
61 |
62 | "animate-ping"
63 | [".animate-ping"
64 | [{:animation "ping 1s cubic-bezier(0,0,0.2,1) infinite"}
65 | #garden.types.CSSAtRule{:identifier :keyframes
66 | :value {:identifier "ping"
67 | :frames (["75%" "100%" {:transform "scale(2)", :opacity 0}])}}]]
68 |
69 | "animate-pulse"
70 | [".animate-pulse"
71 | [{:animation "pulse 2s cubic-bezier(0.4,0,0.6,1) infinite"}
72 | #garden.types.CSSAtRule{:identifier :keyframes
73 | :value {:identifier "pulse"
74 | :frames (["0%" "100%" {:opacity 1}] ["50%" {:opacity 0.5}])}}]]
75 |
76 | "animate-bounce"
77 | [".animate-bounce"
78 | [{:animation "bounce 1s infinite"}
79 | #garden.types.CSSAtRule{:identifier :keyframes
80 | :value {:identifier "bounce"
81 | :frames (["0%"
82 | "100%"
83 | {:transform "translateY(-25%)"
84 | :animation-timing-function "cubic-bezier(0.8,0,1,1)"}]
85 | ["50%"
86 | {:transform "translateY(0)"
87 | :animation-timing-function "cubic-bezier(0,0,0.2,1)"}])}}]]))
88 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/background_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.background-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "bg-transparent"
10 | [".bg-transparent" {:background-color "transparent"}]
11 |
12 | "bg-green-300"
13 | [".bg-green-300" {:--gi-bg-opacity 1
14 | :background-color "rgba(134,239,172,var(--gi-bg-opacity))"}]
15 |
16 | "bg-repeat-round"
17 | [".bg-repeat-round" {:background-repeat "round"}]
18 |
19 | "bg-auto"
20 | [".bg-auto" {:background-size "auto"}]
21 |
22 | "bg-size-auto-auto"
23 | [".bg-size-auto-auto" {:background-size [["auto" "auto"]]}]
24 |
25 | "bg-size-10px-20px"
26 | [".bg-size-10px-20px" {:background-size [["10px" "20px"]]}]
27 |
28 | "bg-size-px-20%"
29 | [".bg-size-px-20\\%" {:background-size [["1px" "20%"]]}]
30 |
31 | "bg-none"
32 | [".bg-none" {:background-image "none"}]
33 |
34 | "bg-gradient-to-r"
35 | [".bg-gradient-to-r" {:background-image "linear-gradient(to right,var(--gi-gradient-stops))"}]
36 |
37 | "bg-gradient-to-tr"
38 | [".bg-gradient-to-tr" {:background-image "linear-gradient(to top right,var(--gi-gradient-stops))"}]
39 |
40 | "bg-origin-border"
41 | [".bg-origin-border" {:background-origin "border-box"}]
42 |
43 | "bg-origin-padding"
44 | [".bg-origin-padding" {:background-origin "padding-box"}]
45 |
46 | "bg-origin-content"
47 | [".bg-origin-content" {:background-origin "content-box"}]
48 |
49 | "from-blue-500"
50 | [".from-blue-500" {:--gi-gradient-from "#3b82f6"
51 | :--gi-gradient-stops "var(--gi-gradient-from),var(--gi-gradient-to,#3b82f600)"}]
52 |
53 | "to-red-500"
54 | [".to-red-500" {:--gi-gradient-to "#ef4444"}]
55 |
56 | "via-white"
57 | [".via-white" {:--gi-gradient-stops "var(--gi-gradient-from),#ffffff,var(--gi-gradient-to,#ffffff00)"}]))
58 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/border_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.border-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 | ;; Border Radius
9 | "rounded-none"
10 | [".rounded-none" {:border-radius "0px"}]
11 |
12 | "rounded"
13 | [".rounded" {:border-radius "0.25rem"}]
14 |
15 | "rounded-xl"
16 | [".rounded-xl" {:border-radius "0.75rem"}]
17 |
18 | "rounded-t-sm"
19 | [".rounded-t-sm" {:border-top-left-radius "0.125rem"
20 | :border-top-right-radius "0.125rem"}]
21 |
22 | "rounded-tl-full"
23 | [".rounded-tl-full" {:border-top-left-radius "9999px"}]
24 |
25 | ;; Border Width
26 | "border"
27 | [".border" {:border-width "1px"}]
28 |
29 | "border-2"
30 | [".border-2" {:border-width "2px"}]
31 |
32 | "border-t"
33 | [".border-t" {:border-top-width "1px"}]
34 |
35 | "border-l-4"
36 | [".border-l-4" {:border-left-width "4px"}]
37 |
38 | "border-b-2em"
39 | [".border-b-2em" {:border-bottom-width "2em"}]
40 |
41 | "border-y-2em"
42 | [".border-y-2em" {:border-top-width "2em"
43 | :border-bottom-width "2em"}]
44 |
45 | "border-3px"
46 | [".border-3px" {:border-width "3px"}]
47 |
48 | ;; Border Color
49 | "border-transparent"
50 | [".border-transparent" {:border-color "transparent"}]
51 |
52 | "border-green-300"
53 | [".border-green-300" {:--gi-border-opacity 1
54 | :border-color "rgba(134,239,172,var(--gi-border-opacity))"}]
55 |
56 | "border-t-green-300"
57 | [".border-t-green-300"
58 | {:--gi-border-opacity 1
59 | :border-top-color "rgba(134,239,172,var(--gi-border-opacity))"}]
60 |
61 | "border-y-green-300"
62 | [".border-y-green-300"
63 | {:--gi-border-opacity 1
64 | :border-bottom-color "rgba(134,239,172,var(--gi-border-opacity))"
65 | :border-top-color "rgba(134,239,172,var(--gi-border-opacity))"}]
66 |
67 | "border-b-rgba-c0ffee50"
68 | [".border-b-rgba-c0ffee50" {:border-bottom-color "#c0ffee50"}]
69 |
70 | ;; Border Opacity
71 | "border-opacity-25"
72 | [".border-opacity-25" {:--gi-border-opacity 0.25}]
73 |
74 | ;; Border Style
75 | "border-dotted"
76 | [".border-dotted" {:border-style "dotted"}]
77 |
78 | "border-hidden"
79 | [".border-hidden" {:border-style "hidden"}]
80 |
81 | ;; Divide Width
82 | "divide-x"
83 | [".divide-x" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
84 | {:--gi-divide-x-reverse 0
85 | :border-left-width "calc(1px * calc(1 - var(--gi-divide-x-reverse)))"
86 | :border-right-width "calc(1px * var(--gi-divide-x-reverse))"}]]
87 |
88 | "divide-y-2"
89 | [".divide-y-2" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
90 | {:--gi-divide-y-reverse 0
91 | :border-top-width "calc(2px * calc(1 - var(--gi-divide-y-reverse)))"
92 | :border-bottom-width "calc(2px * var(--gi-divide-y-reverse))"}]]
93 |
94 | "divide-y-reverse"
95 | [".divide-y-reverse" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
96 | {:--gi-divide-y-reverse 1}]]
97 |
98 | ;; Divide Color
99 | "divide-current"
100 | [".divide-current" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
101 | {:border-color "currentColor"}]]
102 |
103 | "divide-gray-100"
104 | [".divide-gray-100" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
105 | {:--gi-divide-opacity 1
106 | :border-color "rgba(243,244,246,var(--gi-divide-opacity))"}]]
107 |
108 | ;; Divide Opacity
109 | "divide-opacity-70"
110 | [".divide-opacity-70" {:--gi-divide-opacity 0.7}]
111 |
112 | ;; Divide Style
113 | "divide-double"
114 | [".divide-double" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])")
115 | {:border-style "double"}]]
116 |
117 | ;; Outline width
118 | "outline-0"
119 | [".outline-0" {:outline-width "0px"}]
120 |
121 | "outline-3"
122 | [".outline-3" {:outline-width "3px"}]
123 |
124 | "outline-1rem"
125 | [".outline-1rem" {:outline-width "1rem"}]
126 |
127 | "outline-rem"
128 | [".outline-rem" {:outline-width "1rem"}]
129 |
130 | ;; Outline color
131 | "outline-inherit"
132 | [".outline-inherit" {:outline-color "inherit"}]
133 |
134 | "outline-gray-100"
135 | [".outline-gray-100" {:outline-color "#f3f4f6"}]
136 |
137 | ;; Outline style
138 | "outline-none"
139 | [".outline-none" {:outline "2px solid transparent"
140 | :outline-offset "2px"}]
141 |
142 | "outline"
143 | [".outline" {:outline-style "solid"}]
144 |
145 | ;; Outline offset
146 | "outline-offset-0"
147 | [".outline-offset-0" {:outline-offset "0px"}]
148 |
149 | "outline-offset-2"
150 | [".outline-offset-2" {:outline-offset "2px"}]
151 |
152 | "outline-offset-2rem"
153 | [".outline-offset-2rem" {:outline-offset "2rem"}]
154 |
155 | "outline-offset-cm"
156 | [".outline-offset-cm" {:outline-offset "1cm"}]
157 |
158 | ;; Ring Width
159 | ;; TODO: Test for `* box-shadow: 0 0 #0000;`
160 | "ring"
161 | [".ring" {:box-shadow "var(--gi-ring-inset) 0 0 0 calc(3px + var(--gi-ring-offset-width)) var(--gi-ring-color)"}]
162 | "ring-4"
163 | [".ring-4" {:box-shadow "var(--gi-ring-inset) 0 0 0 calc(4px + var(--gi-ring-offset-width)) var(--gi-ring-color)"}]
164 | "ring-inset"
165 | [".ring-inset" {:--gi-ring-inset "inset"}]
166 |
167 | ;; Ring Color
168 | "ring-pink-400"
169 | [".ring-pink-400", {:--gi-ring-color "rgba(244,114,182,var(--gi-ring-opacity))"}]
170 |
171 | ;; Ring Opacity
172 | "ring-opacity-50"
173 | [".ring-opacity-50" {:--gi-ring-opacity 0.5}]
174 |
175 | ;; Ring Offset Width
176 | "ring-offset-1"
177 | [".ring-offset-1" {:--gi-ring-offset-width "1px"
178 | :box-shadow "0 0 0 var(--gi-ring-offset-width) var(--gi-ring-offset-color), var(--gi-ring-shadow)"}]
179 |
180 | ;; Ring Offset Color
181 | "ring-offset-yellow-300"
182 | [".ring-offset-yellow-300" {:--gi-ring-offset-color "rgba(253,224,71,var(--gi-ring-opacity))"
183 | :box-shadow "0 0 0 var(--gi-ring-offset-width) var(--gi-ring-offset-color), var(--gi-ring-shadow)"}]))
184 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/box_alignment_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.box-alignment-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "justify-start"
10 | [".justify-start" {:justify-content "flex-start"}]
11 |
12 | "justify-items-stretch"
13 | [".justify-items-stretch" {:justify-items "stretch"}]
14 |
15 | "justify-self-auto"
16 | [".justify-self-auto" {:justify-self "auto"}]
17 |
18 | "content-evenly"
19 | [".content-evenly" {:align-content "space-evenly"}]
20 |
21 | "items-baseline"
22 | [".items-baseline" {:align-items "baseline"}]
23 |
24 | "self-baseline"
25 | [".self-baseline" {:align-self "baseline"}]
26 |
27 | "self-center"
28 | [".self-center" {:align-self "center"}]
29 |
30 | "self-baseline"
31 | [".self-baseline" {:align-self "baseline"}]
32 |
33 | "place-content-between"
34 | [".place-content-between" {:place-content "space-between"}]
35 |
36 | "place-items-auto"
37 | [".place-items-auto" {:place-items "auto"}]
38 |
39 | "place-self-center"
40 | [".place-self-center" {:place-self "center"}]))
41 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/color_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.color-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.color :as color :refer [read-color flatten-color-map]]))
4 |
5 | (deftest read-color-test
6 | (are [color-data expected-output]
7 | (= expected-output (read-color (flatten-color-map color/tw-v2-colors) color-data))
8 |
9 | [:color [:color-rgb "123"]]
10 | [0x11 0x22 0x33 nil]
11 |
12 | [:color [:color-rgb "112233"]]
13 | [0x11 0x22 0x33 nil]
14 |
15 | [:color [:color-rgba "1234"]]
16 | [0x11 0x22 0x33 0x44]
17 |
18 | [:color [:color-rgba "11223344"]]
19 | [0x11 0x22 0x33 0x44]
20 |
21 | [:color [:color-hsl [:number "0"]
22 | [:number "100"]
23 | [:number "50"]]]
24 | [0xff 0 0 nil]
25 |
26 | [:color [:color-hsl [:number "120"]
27 | [:number "100"]
28 | [:number "50"]]]
29 | [0 0xff 0 nil]
30 |
31 | [:color [:color-hsl [:number "-120"]
32 | [:number "100"]
33 | [:number "50"]]]
34 | [0 0 0xff nil]
35 |
36 | [:color [:color-hsla [:number "0"]
37 | [:number "100"]
38 | [:number "50"]
39 | [:number "0"]]]
40 | [0xff 0 0 0]
41 |
42 | [:color [:color-hsla [:number "0"]
43 | [:number "100"]
44 | [:number "50"]
45 | [:number "50"]]]
46 | [0xff 0 0 0x7f]
47 |
48 | [:color [:color-hsla [:number "0"]
49 | [:number "100"]
50 | [:number "50"]
51 | [:number "100"]]]
52 | [0xff 0 0 0xff]
53 |
54 | [:color [:special-color "transparent"]]
55 | "transparent"
56 |
57 | [:color [:special-color "current"]]
58 | "currentColor"
59 |
60 | [:color [:predefined-color-opacity "green-300"]]
61 | [134 239 172 nil]
62 |
63 | [:color [:predefined-color-opacity "green-300" [:integer "50"]]]
64 | [134 239 172 127]
65 |
66 | [:color [:predefined-color-opacity "green-300" [:number "95_5"]]]
67 | [134 239 172 243]))
68 |
69 |
70 | (deftest read-tw3-color-test
71 | (are [color-data expected-output]
72 | (= expected-output (read-color (flatten-color-map color/tw-v3-unified-colors) color-data))
73 |
74 | [:color [:predefined-color-opacity "slate-300"]]
75 | [203 213 225 nil]
76 |
77 | [:color [:predefined-color-opacity "slate-300" [:integer "50"]]]
78 | [203 213 225 127]
79 |
80 | [:color [:predefined-color-opacity "slate-300" [:number "95_5"]]]
81 | [203 213 225 243]))
82 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/common_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.common-test
2 | (:require
3 | [clojure.test :refer [deftest testing is are]]
4 | [girouette.tw.common :refer [dot read-number number->double-or-int value-unit->css
5 | div-100 div-4 mul-100]]
6 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
7 |
8 | (deftest dot-test
9 | (are [input expected-output]
10 | (= expected-output (dot input))
11 |
12 | "abc-_-de%f" ".abc-_-de\\%f"
13 | "taiwan-#1" ".taiwan-\\#1"))
14 |
15 |
16 | (deftest read-number-test
17 | (are [input expected-output]
18 | (= expected-output (read-number input))
19 |
20 | [:integer "2"] 2
21 | [:number "2"] 2
22 | [:number "2_5"] 2.5
23 | [:number "2.5"] 2.5
24 |
25 | "2" 2
26 | "2_5" 2.5
27 | "2.5" 2.5))
28 |
29 |
30 | (deftest number->double-or-int-test
31 | (are [input expected-output]
32 | (= expected-output (number->double-or-int input))
33 |
34 | 2.5 2.5
35 | 5 5
36 |
37 | ;; Note: ratio numbers do not exist in CLJS.
38 | (/ 10 4) 2.5
39 | (/ 10 2) 5))
40 |
41 |
42 | (deftest value-unit->css-test
43 | (are [data options expected-output]
44 | (= expected-output (value-unit->css data options))
45 |
46 | [:auto] {} "auto"
47 | [:auto] {:signus "-"} "auto"
48 | [:full] {} "full"
49 | [:full] {:signus "-"} "full"
50 |
51 | [:full-100%] {} "100%"
52 | [:screen-100vw] {} "100vw"
53 | [:screen-100vh] {} "100vh"
54 | [:screen-100vh] {:signus "-"} "-100vh"
55 |
56 | [:integer "1"] {} 1
57 | [:number "1"] {} 1
58 | [:integer "1"] {:unit "foo"} "1foo"
59 | [:integer "1"] {:integer {:unit "foo"}} "1foo"
60 | [:integer "70"] {:value-fn div-100} 0.7
61 | [:integer "100"] {:value-fn div-100} 1
62 | [:number "1_5"] {} 1.5
63 | [:number "1.5"] {} 1.5
64 | [:number "1.5"] {:signus "-"} -1.5
65 | [:number "100_5"] {:value-fn div-100} 1.005
66 | [:number "1_5"] {:number {:unit "foo"}} "1.5foo"
67 | [:number "1.5"] {:number {:unit "foo"}} "1.5foo"
68 | [:number "1.5"] {:signus "-"
69 | :unit "foo"} "-1.5foo"
70 |
71 | [:length [:number "0"] "cm"] {} "0cm"
72 | [:length [:number "0"] "cm"] {:zero-unit nil} 0
73 | [:length [:number "0"] "cm"] {:length {:zero-unit nil}} 0
74 | [:length [:number "0"] "cm"] {:signus "-"} "0cm"
75 | [:length [:number "0"] "cm"] {:signus "-"
76 | :zero-unit nil} 0
77 | [:length [:number "0"] "cm"] {:zero-unit "banana"} "0banana"
78 | [:length [:number "0"] "cm"] {:signus "-"
79 | :zero-unit "banana"} "0banana"
80 | [:length [:number "1_5"] "cm"] {} "1.5cm"
81 | [:length-unit "cm"] {} "1cm"
82 | [:length-unit "cm"] {:signus "-"} "-1cm"
83 |
84 | [:percentage [:number "1_5"]] {} "1.5%"
85 | [:percentage [:number "1_5"]] {:signus "-"} "-1.5%"
86 |
87 | [:fraction [:number "5"] [:number "2_5"]] {} 2
88 | [:fraction [:number "5"] [:number "2_5"]] {:fraction {:unit "px"}} "2px"
89 | [:fraction [:number "5"] [:number "2_5"]] {:fraction {:unit "rem"
90 | :value-fn div-4}} "0.5rem"
91 | [:fraction [:number "5"] [:number "2_5"]] {:signus "-"
92 | :fraction {:unit "rem"
93 | :value-fn div-4}} "-0.5rem"
94 | [:fraction [:number "5"] [:number "2_5"]] {:fraction {:unit "%"
95 | :value-fn mul-100}} "200%"
96 | [:fraction [:number "5"] [:number "2_5"]] {:fraction {:unit "apple"
97 | :zero-unit "banana"}} "2apple"
98 | [:fraction [:number "0"] [:number "2_5"]] {:fraction {:unit "apple"
99 | :zero-unit "banana"}} "0banana"
100 | [:fraction [:number "0"] [:number "-2_5"]] {:signus "-"
101 | :fraction {:unit "apple"
102 | :zero-unit "banana"}} "0banana"))
103 |
104 |
105 | (deftest prefixes-test
106 | (are [class-name expected-garden]
107 | (= expected-garden (tw-v3-class-name->garden class-name))
108 |
109 | "group-hover:container"
110 | [".group:hover" [".group-hover\\:container" {:width "100%"}]]
111 |
112 | "group-invalid:container"
113 | [".group:invalid" [".group-invalid\\:container" {:width "100%"}]]
114 |
115 | "group-odd:container"
116 | [".group:nth-child(odd)" [".group-odd\\:container" {:width "100%"}]]
117 |
118 | "peer-active:container"
119 | [".peer:active ~ .peer-active\\:container" {:width "100%"}]
120 |
121 | "peer-odd:container"
122 | [".peer:nth-child(odd) ~ .peer-odd\\:container"
123 | {:width "100%"}]
124 |
125 | "dark:container"
126 | #garden.types.CSSAtRule{:identifier :media
127 | :value {:media-queries {:prefers-color-scheme "dark"}
128 | :rules ([".dark\\:container" {:width "100%"}])}}
129 |
130 | "light:container"
131 | #garden.types.CSSAtRule{:identifier :media
132 | :value {:media-queries {:prefers-color-scheme "light"}
133 | :rules ([".light\\:container" {:width "100%"}])}}
134 |
135 | "motion-safe:container"
136 | #garden.types.CSSAtRule{:identifier :media
137 | :value {:media-queries {:prefers-reduced-motion "no-preference"}
138 | :rules ([".motion-safe\\:container" {:width "100%"}])}}
139 |
140 | "motion-reduce:container"
141 | #garden.types.CSSAtRule{:identifier :media
142 | :value {:media-queries {:prefers-reduced-motion "reduced"}
143 | :rules ([".motion-reduce\\:container" {:width "100%"}])}}
144 |
145 | "landscape:container"
146 | #garden.types.CSSAtRule{:identifier :media
147 | :value {:media-queries {:orientation "landscape"}
148 | :rules ([".landscape\\:container" {:width "100%"}])}}
149 |
150 | "file:container"
151 | [".file\\:container"
152 | [(keyword "&::file-selector-button") {:width "100%"}]]
153 |
154 | "file:hover:container"
155 | [".file\\:hover\\:container"
156 | [(keyword "&::file-selector-button")
157 | [:&:hover {:width "100%"}]]]
158 |
159 | "hover:file:container"
160 | [".hover\\:file\\:container"
161 | [:&:hover
162 | [(keyword "&::file-selector-button") {:width "100%"}]]]
163 |
164 | "open:bg-white"
165 | [".open\\:bg-white"
166 | [(keyword "&[open]") {:background-color "rgba(255,255,255,var(--gi-bg-opacity))"
167 | :--gi-bg-opacity 1}]]
168 |
169 | "hover:file:container"
170 | [".hover\\:file\\:container"
171 | [:&:hover
172 | [(keyword "&::file-selector-button") {:width "100%"}]]]
173 |
174 | "focus:container"
175 | [".focus\\:container" [:&:focus {:width "100%"}]]
176 |
177 | "first:container"
178 | [".first\\:container" [:&:first-child {:width "100%"}]]
179 |
180 | "last:container"
181 | [".last\\:container" [:&:last-child {:width "100%"}]]
182 |
183 | "odd:container"
184 | [".odd\\:container" [(keyword "&:nth-child(odd)") {:width "100%"}]]
185 |
186 | "even:container"
187 | [".even\\:container" [(keyword "&:nth-child(even)") {:width "100%"}]]
188 |
189 | "invalid:bg-#d66f"
190 | [".invalid\\:bg-\\#d66f" [:&:invalid {:background-color "#dd6666ff"}]]
191 |
192 | "sm:focus:container"
193 | #garden.types.CSSAtRule{:identifier :media
194 | :value {:media-queries {:min-width "640px"}
195 | :rules ([".sm\\:focus\\:container" [:&:focus {:max-width "640px"}]])}}
196 |
197 | "sm:first:focus:container"
198 | #garden.types.CSSAtRule{:identifier :media
199 | :value {:media-queries {:min-width "640px"}
200 | :rules ([".sm\\:first\\:focus\\:container" [:&:first-child [:&:focus {:max-width "640px"}]]])}}
201 |
202 | "placeholder:text-red-500"
203 | [".placeholder\\:text-red-500" [(keyword "&::placeholder")
204 | {:color "rgba(239,68,68,var(--gi-text-opacity))"
205 | :--gi-text-opacity 1}]]
206 |
207 | "before:ml-1"
208 | [".before\\:ml-1" [(keyword "&::before") {:margin-left "0.25rem"}]]))
209 |
210 | (deftest arbitrary-test
211 | (are [class-name expected-garden]
212 | (= expected-garden (tw-v3-class-name->garden class-name))
213 |
214 | "hover:[mask-type:alpha]"
215 | [".hover\\:\\[mask-type\\:alpha\\]"
216 | [:&:hover {:mask-type "alpha"}]]))
217 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/core_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.core-test
2 | (:require
3 | [clojure.test :refer [deftest testing is are]]
4 | [girouette.tw.layout :as layout]
5 | [girouette.tw.core :as gc]))
6 |
7 | (deftest assoc-ordering-level-test
8 | (let [id->component {1 {:before-rules #{2 3}}
9 | 2 {}
10 | 3 {}
11 | 4 {:after-rules #{3}}
12 | 5 {}}]
13 | (testing "The :before-rules and :after-rules should become symmetric."
14 | (is (= (-> id->component
15 | (#'gc/complement-before-rules-after-rules))
16 | {1 {:before-rules #{2 3}}
17 | 2 {:after-rules #{1}}
18 | 3 {:before-rules #{4}
19 | :after-rules #{1}}
20 | 4 {:after-rules #{3}}
21 | 5 {}})))
22 |
23 | (testing "The ordering levels should be correct."
24 | (is (= (-> id->component
25 | (#'gc/complement-before-rules-after-rules)
26 | (#'gc/assoc-ordering-level))
27 | {1 {:ordering-level 0
28 | :before-rules #{2 3}}
29 | 2 {:ordering-level 1
30 | :after-rules #{1}}
31 | 3 {:ordering-level 1
32 | :after-rules #{1}
33 | :before-rules #{4}}
34 | 4 {:ordering-level 2
35 | :after-rules #{3}}
36 | 5 {:ordering-level 0}})))))
37 |
38 | (deftest make-api-test
39 | (testing "The API still work without any color and font family."
40 | (let [{:keys [class-name->garden]} (gc/make-api layout/components {})]
41 | (is (= [".flex" {:display "flex"}]
42 | (class-name->garden "flex"))))))
43 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/effect_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.effect-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v2-class-name->garden
4 | tw-v3-class-name->garden]]))
5 |
6 | (deftest component-v2-test
7 | (are [class-name expected-garden]
8 | (= expected-garden (tw-v2-class-name->garden class-name))
9 |
10 | ;; Box shadow v2
11 | "shadow"
12 | [".shadow" {:--gi-shadow "0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px 0 rgba(0,0,0,0.06)"
13 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
14 |
15 | "shadow-none"
16 | [".shadow-none" {:--gi-shadow "0 0 #0000"
17 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
18 |
19 | "shadow-inner"
20 | [".shadow-inner" {:--gi-shadow "inset 0 2px 4px 0 rgba(0,0,0,0.06)"
21 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
22 |
23 | "shadow-2xl"
24 | [".shadow-2xl" {:--gi-shadow "0 25px 50px -12px rgba(0,0,0,0.25)"
25 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]))
26 |
27 |
28 | ;; The v3 components which are incompatible with v2.
29 | (deftest component-v3-test
30 | (are [class-name expected-garden]
31 | (= expected-garden (tw-v3-class-name->garden class-name))
32 |
33 | ;; Box shadow v3
34 | "shadow"
35 | [".shadow" {:--gi-shadow "0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px -1px rgba(0,0,0,0.1)"
36 | :--gi-shadow-colored "0 1px 3px 0 var(--gi-shadow-color),0 1px 2px -1px var(--gi-shadow-color)"
37 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
38 |
39 | "shadow-none"
40 | [".shadow-none" {:--gi-shadow "0 0 #0000",
41 | :--gi-shadow-colored "0 0 var(--gi-shadow-color)",
42 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
43 |
44 | "shadow-inner"
45 | [".shadow-inner" {:--gi-shadow "inset 0 2px 4px 0 rgba(0,0,0,0.05)",
46 | :--gi-shadow-colored "inset 0 2px 4px 0 var(--gi-shadow-color)"
47 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
48 |
49 | "shadow-2xl"
50 | [".shadow-2xl" {:--gi-shadow "0 25px 50px -12px rgba(0,0,0,0.25)",
51 | :--gi-shadow-colored "0 25px 50px -12px var(--gi-shadow-color)"
52 | :box-shadow "var(--gi-ring-offset-shadow,0 0 #0000),var(--gi-ring-shadow,0 0 #0000),var(--gi-shadow)"}]
53 |
54 | ;; Box shadow color
55 | "shadow-inherit"
56 | [".shadow-inherit" {:--gi-shadow-color "inherit"
57 | :--gi-shadow "var(--gi-shadow-colored)"}]
58 |
59 | "shadow-current"
60 | [".shadow-current" {:--gi-shadow-color "currentColor"
61 | :--gi-shadow "var(--gi-shadow-colored)"}]
62 |
63 | "shadow-cyan-500-50"
64 | [".shadow-cyan-500-50" {:--gi-shadow-color "#06b6d47f"
65 | :--gi-shadow "var(--gi-shadow-colored)"}]
66 |
67 | "shadow-cyan-500/50"
68 | [".shadow-cyan-500\\/50" {:--gi-shadow-color "#06b6d47f"
69 | :--gi-shadow "var(--gi-shadow-colored)"}]
70 |
71 | ;; Opacity
72 | "opacity-0"
73 | [".opacity-0" {:opacity 0}]
74 |
75 | "opacity-20"
76 | [".opacity-20" {:opacity 0.2}]
77 |
78 | "opacity-100"
79 | [".opacity-100" {:opacity 1}]
80 |
81 | ;; Mix Blend Color
82 | "mix-blend-normal"
83 | [".mix-blend-normal" {:mix-blend-mode "normal"}]
84 |
85 | "mix-blend-multiply"
86 | [".mix-blend-multiply" {:mix-blend-mode "multiply"}]
87 |
88 | "mix-blend-screen"
89 | [".mix-blend-screen" {:mix-blend-mode "screen"}]
90 |
91 | "mix-blend-overlay"
92 | [".mix-blend-overlay" {:mix-blend-mode "overlay"}]
93 |
94 | "mix-blend-darken"
95 | [".mix-blend-darken" {:mix-blend-mode "darken"}]
96 |
97 | "mix-blend-lighten"
98 | [".mix-blend-lighten" {:mix-blend-mode "lighten"}]
99 |
100 | "mix-blend-color-dodge"
101 | [".mix-blend-color-dodge" {:mix-blend-mode "color-dodge"}]
102 |
103 | "mix-blend-color-burn"
104 | [".mix-blend-color-burn" {:mix-blend-mode "color-burn"}]
105 |
106 | "mix-blend-hard-light"
107 | [".mix-blend-hard-light" {:mix-blend-mode "hard-light"}]
108 |
109 | "mix-blend-soft-light"
110 | [".mix-blend-soft-light" {:mix-blend-mode "soft-light"}]
111 |
112 | "mix-blend-difference"
113 | [".mix-blend-difference" {:mix-blend-mode "difference"}]
114 |
115 | "mix-blend-exclusion"
116 | [".mix-blend-exclusion" {:mix-blend-mode "exclusion"}]
117 |
118 | "mix-blend-hue"
119 | [".mix-blend-hue" {:mix-blend-mode "hue"}]
120 |
121 | "mix-blend-saturation"
122 | [".mix-blend-saturation" {:mix-blend-mode "saturation"}]
123 |
124 | "mix-blend-color"
125 | [".mix-blend-color" {:mix-blend-mode "color"}]
126 |
127 | "mix-blend-luminosity"
128 | [".mix-blend-luminosity" {:mix-blend-mode "luminosity"}]
129 |
130 | ;; background blend mode
131 | "bg-blend-normal"
132 | [".bg-blend-normal" {:background-blend-mode "normal"}]
133 |
134 | "bg-blend-multiply"
135 | [".bg-blend-multiply" {:background-blend-mode "multiply"}]
136 |
137 | "bg-blend-screen"
138 | [".bg-blend-screen" {:background-blend-mode "screen"}]
139 |
140 | "bg-blend-overlay"
141 | [".bg-blend-overlay" {:background-blend-mode "overlay"}]
142 |
143 | "bg-blend-darken"
144 | [".bg-blend-darken" {:background-blend-mode "darken"}]
145 |
146 | "bg-blend-lighten"
147 | [".bg-blend-lighten" {:background-blend-mode "lighten"}]
148 |
149 | "bg-blend-color-dodge"
150 | [".bg-blend-color-dodge" {:background-blend-mode "color-dodge"}]
151 |
152 | "bg-blend-color-burn"
153 | [".bg-blend-color-burn" {:background-blend-mode "color-burn"}]
154 |
155 | "bg-blend-hard-light"
156 | [".bg-blend-hard-light" {:background-blend-mode "hard-light"}]
157 |
158 | "bg-blend-soft-light"
159 | [".bg-blend-soft-light" {:background-blend-mode "soft-light"}]
160 |
161 | "bg-blend-difference"
162 | [".bg-blend-difference" {:background-blend-mode "difference"}]
163 |
164 | "bg-blend-exclusion"
165 | [".bg-blend-exclusion" {:background-blend-mode "exclusion"}]
166 |
167 | "bg-blend-hue"
168 | [".bg-blend-hue" {:background-blend-mode "hue"}]
169 |
170 | "bg-blend-saturation"
171 | [".bg-blend-saturation" {:background-blend-mode "saturation"}]
172 |
173 | "bg-blend-color"
174 | [".bg-blend-color" {:background-blend-mode "color"}]
175 |
176 | "bg-blend-luminosity"
177 | [".bg-blend-luminosity" {:background-blend-mode "luminosity"}]))
178 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/filter_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.filter-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.filter :as filter]
4 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
5 |
6 | (deftest component-test
7 | (are [class-name expected-garden]
8 | (= expected-garden (tw-v3-class-name->garden class-name))
9 |
10 | ;; Blur
11 | "blur-none"
12 | [".blur-none" {:--gi-blur "blur(0)"
13 | :filter @#'filter/filter-rule}]
14 |
15 | "blur"
16 | [".blur" {:--gi-blur "blur(8px)"
17 | :filter @#'filter/filter-rule}]
18 |
19 | "blur-lg"
20 | [".blur-lg" {:--gi-blur "blur(16px)"
21 | :filter @#'filter/filter-rule}]
22 |
23 | "blur-1rem"
24 | [".blur-1rem" {:--gi-blur "blur(1rem)"
25 | :filter @#'filter/filter-rule}]
26 |
27 | "blur-1px"
28 | [".blur-1px" {:--gi-blur "blur(1px)"
29 | :filter @#'filter/filter-rule}]
30 |
31 | ;; Brightness
32 | "brightness-0"
33 | [".brightness-0" {:--gi-brightness "brightness(0)"
34 | :filter @#'filter/filter-rule}]
35 |
36 | "brightness-75"
37 | [".brightness-75" {:--gi-brightness "brightness(0.75)"
38 | :filter @#'filter/filter-rule}]
39 |
40 | "brightness-75%"
41 | [".brightness-75\\%" {:--gi-brightness "brightness(75%)"
42 | :filter @#'filter/filter-rule}]
43 |
44 | "brightness-150"
45 | [".brightness-150" {:--gi-brightness "brightness(1.5)"
46 | :filter @#'filter/filter-rule}]
47 |
48 | ;; Contrast
49 | "contrast-0"
50 | [".contrast-0" {:--gi-contrast "contrast(0)"
51 | :filter @#'filter/filter-rule}]
52 |
53 | "contrast-75"
54 | [".contrast-75" {:--gi-contrast "contrast(0.75)"
55 | :filter @#'filter/filter-rule}]
56 |
57 | "contrast-75%"
58 | [".contrast-75\\%" {:--gi-contrast "contrast(75%)"
59 | :filter @#'filter/filter-rule}]
60 |
61 | "contrast-150"
62 | [".contrast-150" {:--gi-contrast "contrast(1.5)"
63 | :filter @#'filter/filter-rule}]
64 |
65 | ;; Drop shadow
66 | "drop-shadow"
67 | [".drop-shadow"
68 | {:--gi-drop-shadow "drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06))"
69 | :filter @#'filter/filter-rule}]
70 |
71 | "drop-shadow-lg"
72 | [".drop-shadow-lg"
73 | {:--gi-drop-shadow "drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1))"
74 | :filter @#'filter/filter-rule}]
75 |
76 | ;; Grayscale
77 | "grayscale"
78 | [".grayscale" {:--gi-grayscale "grayscale(100%)"
79 | :filter @#'filter/filter-rule}]
80 |
81 | "grayscale-0"
82 | [".grayscale-0" {:--gi-grayscale "grayscale(0)"
83 | :filter @#'filter/filter-rule}]
84 |
85 | "grayscale-1/2"
86 | [".grayscale-1\\/2" {:--gi-grayscale "grayscale(0.5)"
87 | :filter @#'filter/filter-rule}]
88 |
89 | "grayscale-50"
90 | [".grayscale-50" {:--gi-grayscale "grayscale(0.5)"
91 | :filter @#'filter/filter-rule}]
92 |
93 | "grayscale-100%"
94 | [".grayscale-100\\%" {:--gi-grayscale "grayscale(100%)"
95 | :filter @#'filter/filter-rule}]
96 |
97 | ;; Hue rotate
98 | "hue-rotate-30"
99 | [".hue-rotate-30" {:--gi-hue-rotate "hue-rotate(30deg)"
100 | :filter @#'filter/filter-rule}]
101 |
102 | "hue-rotate-30deg"
103 | [".hue-rotate-30deg" {:--gi-hue-rotate "hue-rotate(30deg)"
104 | :filter @#'filter/filter-rule}]
105 |
106 | "hue-rotate-0_5turn"
107 | [".hue-rotate-0_5turn" {:--gi-hue-rotate "hue-rotate(0.5turn)"
108 | :filter @#'filter/filter-rule}]
109 |
110 | ;; Invert
111 | "invert"
112 | [".invert" {:--gi-invert "invert(100%)"
113 | :filter @#'filter/filter-rule}]
114 |
115 | "invert-0"
116 | [".invert-0" {:--gi-invert "invert(0)"
117 | :filter @#'filter/filter-rule}]))
118 |
119 | ;; Saturate
120 | ;; Sepia
121 |
122 | ;; Backdrop blur
123 | ;; Backdrop brightness
124 | ;; Backdrop contrast
125 | ;; Backdrop gray scale
126 | ;; Backdrop hue rotate
127 | ;; Backdrop invert
128 | ;; Backdrop opacity
129 | ;; Backdrop saturate
130 | ;; Backdrop sepia
131 |
132 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/flexbox_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.flexbox-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "flex-grow"
10 | [".flex-grow" {:flex-grow 1}]
11 |
12 | "grow-0"
13 | [".grow-0" {:flex-grow 0}]
14 |
15 | "flex-grow-3"
16 | [".flex-grow-3" {:flex-grow 3}]
17 |
18 | "flex-grow-3/2"
19 | [".flex-grow-3\\/2" {:flex-grow 1.5}]
20 |
21 | "flex-shrink"
22 | [".flex-shrink" {:flex-shrink 1}]
23 |
24 | "flex-shrink-3"
25 | [".flex-shrink-3" {:flex-shrink 3}]
26 |
27 | "flex-basis"
28 | [".flex-basis" {:flex-basis 1}]
29 |
30 | "flex-basis-0"
31 | [".flex-basis-0" {:flex-basis "0px"}]
32 |
33 | "flex-basis-3"
34 | [".flex-basis-3" {:flex-basis "0.75rem"}]
35 |
36 | "flex-basis-px"
37 | [".flex-basis-px" {:flex-basis "1px"}]
38 |
39 | "flex-basis-18px"
40 | [".flex-basis-18px" {:flex-basis "18px"}]
41 |
42 | "flex-basis-auto"
43 | [".flex-basis-auto" {:flex-basis "auto"}]
44 |
45 | "flex-basis-full"
46 | [".flex-basis-full" {:flex-basis "100%"}]
47 |
48 | "flex-0"
49 | [".flex-0" {:flex "0 0 0%"}]
50 |
51 | "flex-1"
52 | [".flex-1" {:flex "1 1 0%"}]
53 |
54 | "flex-2"
55 | [".flex-2" {:flex "2 2 0%"}]
56 |
57 | "flex-4/5"
58 | [".flex-4\\/5" {:flex "0.8 0.8 0%"}]
59 |
60 | "flex-auto"
61 | [".flex-auto" {:flex "1 1 auto"}]
62 |
63 | "flex-initial"
64 | [".flex-initial" {:flex "0 1 auto"}]
65 |
66 | "flex-2-3"
67 | [".flex-2-3" {:flex "2 3"}]
68 |
69 | "flex-2-3%"
70 | [".flex-2-3\\%" {:flex "2 3%"}]
71 |
72 | "flex-2-px"
73 | [".flex-2-px" {:flex "2 1px"}]
74 |
75 | "flex-2-3-4"
76 | [".flex-2-3-4" {:flex "2 3 1rem"}]
77 |
78 | "flex-none"
79 | [".flex-none" {:flex "none"}]
80 |
81 | "flex-col-reverse"
82 | [".flex-col-reverse" {:flex-direction "column-reverse"}]
83 |
84 | "flex-nowrap"
85 | [".flex-nowrap" {:flex-wrap "nowrap"}]
86 |
87 | "order-none"
88 | [".order-none" {:order 0}]
89 |
90 | "-order-1"
91 | [".-order-1" {:order -1}]
92 |
93 | "order-1"
94 | [".order-1" {:order 1}]))
95 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/grid_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.grid-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "grid-cols-none"
10 | [".grid-cols-none" {:grid-template-columns "none"}]
11 | "grid-cols-0"
12 | [".grid-cols-0" {:grid-template-columns "none"}]
13 | "grid-cols-10"
14 | [".grid-cols-10" {:grid-template-columns "repeat(10, minmax(0, 1fr))"}]
15 |
16 | "col-auto"
17 | [".col-auto" {:grid-column "auto"}]
18 |
19 | "col-span-5"
20 | [".col-span-5" {:grid-column "span 5 / span 5"}]
21 | "col-span-full"
22 | [".col-span-full" {:grid-column "-1 / 1"}]
23 |
24 | "col-start-1"
25 | [".col-start-1" {:grid-column-start 1}]
26 | "col-start-auto"
27 | [".col-start-auto" {:grid-column-start "auto"}]
28 |
29 | "col-end-1"
30 | [".col-end-1" {:grid-column-end 1}]
31 | "col-end-auto"
32 | [".col-end-auto" {:grid-column-end "auto"}]
33 |
34 | "grid-rows-none"
35 | [".grid-rows-none" {:grid-template-rows "none"}]
36 | "grid-rows-0"
37 | [".grid-rows-0" {:grid-template-rows "none"}]
38 | "grid-rows-10"
39 | [".grid-rows-10" {:grid-template-rows "repeat(10, minmax(0, 1fr))"}]
40 |
41 | "row-auto"
42 | [".row-auto" {:grid-row "auto"}]
43 |
44 | "row-span-5"
45 | [".row-span-5" {:grid-row "span 5 / span 5"}]
46 | "row-span-full"
47 | [".row-span-full" {:grid-row "-1 / 1"}]
48 |
49 | "row-start-1"
50 | [".row-start-1" {:grid-row-start 1}]
51 | "row-start-auto"
52 | [".row-start-auto" {:grid-row-start "auto"}]
53 |
54 | "row-end-1"
55 | [".row-end-1" {:grid-row-end 1}]
56 | "row-end-auto"
57 | [".row-end-auto" {:grid-row-end "auto"}]
58 |
59 | "grid-flow-row"
60 | [".grid-flow-row" {:grid-auto-flow "row"}]
61 | "grid-flow-col-dense"
62 | [".grid-flow-col-dense" {:grid-auto-flow "column dense"}]
63 |
64 | "auto-cols-auto"
65 | [".auto-cols-auto" {:grid-auto-columns "auto"}]
66 | "auto-cols-min"
67 | [".auto-cols-min" {:grid-auto-columns "min-content"}]
68 |
69 | "auto-rows-auto"
70 | [".auto-rows-auto" {:grid-auto-rows "auto"}]
71 | "auto-rows-min"
72 | [".auto-rows-min" {:grid-auto-rows "min-content"}]
73 |
74 | "gap-0"
75 | [".gap-0" {:gap 0}]
76 | "gap-x-0"
77 | [".gap-x-0" {:column-gap 0}]
78 |
79 | "gap-1"
80 | [".gap-1" {:gap "0.25rem"}]
81 | "gap-y-1"
82 | [".gap-y-1" {:row-gap "0.25rem"}]
83 |
84 | "gap-px"
85 | [".gap-px" {:gap "1px"}]
86 | "gap-y-px"
87 | [".gap-y-px" {:row-gap "1px"}]
88 |
89 | "gap-10%"
90 | [".gap-10\\%" {:gap "10%"}]
91 | "gap-y-10%"
92 | [".gap-y-10\\%" {:row-gap "10%"}]))
93 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/interactivity_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.interactivity-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v2-class-name->garden
4 | tw-v3-class-name->garden]]))
5 |
6 | (deftest component-v2-test
7 | (are [class-name expected-garden]
8 | (= expected-garden (tw-v2-class-name->garden class-name))
9 |
10 | "outline-none"
11 | [".outline-none" {:outline "2px solid transparent"
12 | :outline-offset "2px"}]))
13 |
14 |
15 | (deftest component-v3-test
16 | (are [class-name expected-garden]
17 | (= expected-garden (tw-v3-class-name->garden class-name))
18 |
19 | "accent-current"
20 | [".accent-current" {:accent-color "currentColor"}]
21 |
22 | "accent-#abcdef11"
23 | [".accent-\\#abcdef11" {:accent-color "#abcdef11"}]
24 |
25 | "accent-auto"
26 | [".accent-auto" {:accent-color "auto"}]
27 |
28 | "appearance-none"
29 | [".appearance-none" {:appearance "none"}]
30 |
31 | "cursor-help"
32 | [".cursor-help" {:cursor "help"}]
33 |
34 | "cursor-wait"
35 | [".cursor-wait" {:cursor "wait"}]
36 |
37 | "pointer-events-none"
38 | [".pointer-events-none" {:pointer-events "none"}]
39 |
40 | "resize"
41 | [".resize" {:resize "both"}]
42 |
43 | "resize-x"
44 | [".resize-x" {:resize "horizontal"}]
45 |
46 | "scroll-auto"
47 | [".scroll-auto" {:scroll-behavior "auto"}]
48 |
49 | "scroll-smooth"
50 | [".scroll-smooth" {:scroll-behavior "smooth"}]
51 |
52 | ;; Scroll margin
53 | "scroll-m-8"
54 | [".scroll-m-8" {:scroll-margin "2rem"}]
55 |
56 | "scroll-mt-8"
57 | [".scroll-mt-8" {:scroll-margin-top "2rem"}]
58 |
59 | "scroll-ml-0"
60 | [".scroll-ml-0" {:scroll-margin-left "0px"}]
61 |
62 | "scroll-my-11vh"
63 | [".scroll-my-11vh" {:scroll-margin-top "11vh"
64 | :scroll-margin-bottom "11vh"}]
65 |
66 | "scroll-mx-8"
67 | [".scroll-mx-8" {:scroll-margin-left "2rem"
68 | :scroll-margin-right "2rem"}]
69 |
70 | ;; Scroll padding
71 | "scroll-p-8"
72 | [".scroll-p-8" {:scroll-padding "2rem"}]
73 |
74 | "scroll-p-10%"
75 | [".scroll-p-10\\%" {:scroll-padding "10%"}]
76 |
77 | "scroll-p-1/2"
78 | [".scroll-p-1\\/2" {:scroll-padding "50%"}]
79 |
80 | "scroll-pt-8"
81 | [".scroll-pt-8" {:scroll-padding-top "2rem"}]
82 |
83 | "scroll-px-8"
84 | [".scroll-px-8" {:scroll-padding-left "2rem"
85 | :scroll-padding-right "2rem"}]
86 |
87 | ;; Scroll snap align
88 | "snap-start"
89 | [".snap-start" {:scroll-snap-align "start"}]
90 |
91 | "snap-align-none"
92 | [".snap-align-none" {:scroll-snap-align "none"}]
93 |
94 | ;; Scroll snap stop
95 | "snap-normal"
96 | [".snap-normal" {:scroll-snap-stop "normal"}]
97 |
98 | ;; Scroll snap type
99 | "snap-x"
100 | [".snap-x" {:scroll-snap-type "x var(--gi-scroll-snap-strictness)"}]
101 |
102 | "snap-mandatory"
103 | [".snap-mandatory" {:--gi-scroll-snap-strictness "mandatory"}]
104 |
105 | ;; Touch action
106 | "touch-auto"
107 | [".touch-auto" {:touch-action "auto"}]
108 |
109 | "touch-manipulation"
110 | [".touch-manipulation" {:touch-action "manipulation"}]
111 |
112 | ;; User select
113 | "select-all"
114 | [".select-all" {:user-select "all"}]
115 |
116 | ;; Will change
117 | "will-change-auto"
118 | [".will-change-auto" {:will-change "auto"}]
119 |
120 | "will-change-scroll"
121 | [".will-change-scroll" {:will-change "scroll-position"}]))
122 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/layout_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.layout-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | ;; Aspect Ratio
10 | "aspect-auto"
11 | [".aspect-auto" {:aspect-ratio "auto"}]
12 |
13 | "aspect-square"
14 | [".aspect-square" {:aspect-ratio "1 / 1"}]
15 |
16 | "aspect-video"
17 | [".aspect-video" {:aspect-ratio "16 / 9"}]
18 |
19 | "aspect-23/57"
20 | [".aspect-23\\/57" {:aspect-ratio "23 / 57"}]
21 |
22 | ;; Container
23 | "container"
24 | [".container" {:width "100%"}]
25 |
26 | "sm:container"
27 | #garden.types.CSSAtRule {:identifier :media
28 | :value {:media-queries {:min-width "640px"}
29 | :rules ([".sm\\:container" {:max-width "640px"}])}}
30 |
31 | "md:container"
32 | #garden.types.CSSAtRule {:identifier :media
33 | :value {:media-queries {:min-width "768px"}
34 | :rules ([".md\\:container" {:max-width "768px"}])}}
35 |
36 | ;; Columns
37 | "columns-4"
38 | [".columns-4" {:columns "4"}]
39 |
40 | "columns-auto"
41 | [".columns-auto" {:columns "auto"}]
42 |
43 | "columns-2xl"
44 | [".columns-2xl" {:columns "42rem"}]
45 |
46 | "columns-2-auto"
47 | [".columns-2-auto" {:columns "2 auto"}]
48 |
49 | "columns-2-300px"
50 | [".columns-2-300px" {:columns "2 300px"}]
51 |
52 | "columns-auto-auto"
53 | [".columns-auto-auto" {:columns "auto auto"}]
54 |
55 | ;; Break after
56 | "break-after-auto"
57 | [".break-after-auto" {:break-after "auto"}]
58 |
59 | ;; Break before
60 | "break-before-page"
61 | [".break-before-page" {:break-before "page"}]
62 |
63 | ;; Break inside
64 | "break-inside-avoid-column"
65 | [".break-inside-avoid-column" {:break-inside "avoid-column"}]
66 |
67 | ;; Box decoration break
68 | "box-decoration-clone"
69 | [".box-decoration-clone" {:box-decoration-break "clone"}]
70 |
71 | "box-decoration-slice"
72 | [".box-decoration-slice" {:box-decoration-break "slice"}]
73 |
74 | ;; Box sizing
75 | "box-content"
76 | [".box-content" {:box-sizing "content-box"}]
77 |
78 | ;; Display
79 | "flex"
80 | [".flex" {:display "flex"}]
81 |
82 | "sm:flex"
83 | #garden.types.CSSAtRule {:identifier :media
84 | :value {:media-queries {:min-width "640px"}
85 | :rules ([".sm\\:flex"
86 | {:display "flex"}])}}
87 |
88 | "inline-table"
89 | [".inline-table" {:display "inline-table"}]
90 |
91 | "list-item"
92 | [".list-item" {:display "list-item"}]
93 |
94 | "hidden"
95 | [".hidden" {:display "none"}]
96 |
97 | ;; Floats
98 |
99 | ;; Clear
100 |
101 | ;; Isolation
102 | "isolate"
103 | [".isolate" {:isolation "isolate"}]
104 |
105 | "isolation-auto"
106 | [".isolation-auto" {:isolation "auto"}]
107 |
108 | ;; Object fit
109 |
110 | ;; Object position
111 | "object-left-top"
112 | [".object-left-top" {:object-position "left top"}]
113 |
114 | ;; Overflow
115 | "overflow-hidden"
116 | [".overflow-hidden" {:overflow "hidden"}]
117 |
118 | "overflow-clip"
119 | [".overflow-clip" {:overflow "clip"}]
120 |
121 | "overflow-x-auto"
122 | [".overflow-x-auto" {:overflow-x "auto"}]
123 |
124 | "overflow-x-clip"
125 | [".overflow-x-clip" {:overflow-x "clip"}]
126 |
127 | ;; Overscroll behavior
128 | "overscroll-x-auto"
129 | [".overscroll-x-auto" {:overscroll-x "auto"}]
130 |
131 | "overscroll-none"
132 | [".overscroll-none" {:overscroll "none"}]
133 |
134 | ;; Position
135 |
136 | ;; (Positioning) Top / Right / Bottom / Left
137 | "-inset-3/8"
138 | [".-inset-3\\/8"
139 | {:bottom "-37.5%"
140 | :left "-37.5%"
141 | :right "-37.5%"
142 | :top "-37.5%"}]
143 |
144 | "inset-x-auto"
145 | [".inset-x-auto" {:left "auto"
146 | :right "auto"}]
147 |
148 | "top-0"
149 | [".top-0" {:top 0}]
150 |
151 | "top-0.5"
152 | [".top-0\\.5" {:top "0.125rem"}]
153 |
154 | "top-0_5"
155 | [".top-0_5" {:top "0.125rem"}]
156 |
157 | "top-1"
158 | [".top-1" {:top "0.25rem"}]
159 |
160 | "top-cm"
161 | [".top-cm" {:top "1cm"}]
162 |
163 | "top-1px"
164 | [".top-1px" {:top "1px"}]
165 |
166 | "top-1rem"
167 | [".top-1rem" {:top "1rem"}]
168 |
169 | "top-1%"
170 | [".top-1\\%" {:top "1%"}]
171 |
172 | "top-3/8"
173 | [".top-3\\/8" {:top "37.5%"}]
174 |
175 | "top-full"
176 | [".top-full" {:top "100%"}]
177 |
178 | ;; Visibility
179 | "invisible"
180 | [".invisible" {:visibility "hidden"}]
181 |
182 | ;; Z-index
183 | "z-0"
184 | [".z-0" {:z-index 0}]
185 |
186 | "z-1"
187 | [".z-1" {:z-index 1}]
188 |
189 | "z-auto"
190 | [".z-auto" {:z-index "auto"}]))
191 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/sizing_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.sizing-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "w-10"
10 | [".w-10" {:width "2.5rem"}]
11 |
12 | "w-px"
13 | [".w-px" {:width "1px"}]
14 |
15 | "w-30%"
16 | [".w-30\\%" {:width "30%"}]
17 |
18 | "w-1/4"
19 | [".w-1\\/4" {:width "25%"}]
20 |
21 | "w-auto"
22 | [".w-auto" {:width "auto"}]
23 |
24 | "w-full"
25 | [".w-full" {:width "100%"}]
26 |
27 | "w-screen"
28 | [".w-screen" {:width "100vw"}]
29 |
30 | "w-min"
31 | [".w-min" {:width "min-content"}]
32 |
33 | "w-max"
34 | [".w-max" {:width "max-content"}]
35 |
36 | "min-w-auto"
37 | [".min-w-auto" {:min-width "auto"}]
38 |
39 | "min-w-full"
40 | [".min-w-full" {:min-width "100%"}]
41 |
42 | "min-w-min"
43 | [".min-w-min" {:min-width "min-content"}]
44 |
45 | "min-w-max"
46 | [".min-w-max" {:min-width "max-content"}]
47 |
48 | "max-w-none"
49 | [".max-w-none" {:max-width "none"}]
50 |
51 | "max-w-full"
52 | [".max-w-full" {:max-width "100%"}]
53 |
54 | "max-w-min"
55 | [".max-w-min" {:max-width "min-content"}]
56 |
57 | "max-w-max"
58 | [".max-w-max" {:max-width "max-content"}]
59 |
60 | "max-w-lg"
61 | [".max-w-lg" {:max-width "32rem"}]
62 |
63 | "max-w-prose"
64 | [".max-w-prose" {:max-width "65ch"}]
65 |
66 | "max-w-16rem"
67 | [".max-w-16rem" {:max-width "16rem"}]
68 |
69 | "max-w-screen-sm"
70 | [".max-w-screen-sm" {:max-width "640px"}]
71 |
72 | "max-h-64"
73 | [".max-h-64" {:max-height "16rem"}]
74 |
75 | "max-w-fit"
76 | [".max-w-fit" {:max-width "fit-content"}]
77 |
78 | "h-fit"
79 | [".h-fit" {:height "fit-content"}]))
80 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/spacing_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.spacing-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "p-0"
10 | [".p-0" {:padding 0}]
11 |
12 | "-p-0"
13 | [".-p-0" {:padding 0}]
14 |
15 | "p-1"
16 | [".p-1" {:padding "0.25rem"}]
17 |
18 | "-p-1"
19 | [".-p-1" {:padding "-0.25rem"}]
20 |
21 | "p-2"
22 | [".p-2" {:padding "0.5rem"}]
23 |
24 | "p-px"
25 | [".p-px" {:padding "1px"}]
26 |
27 | "-p-px"
28 | [".-p-px" {:padding "-1px"}]
29 |
30 | "px-2"
31 | [".px-2" {:padding-left "0.5rem"
32 | :padding-right "0.5rem"}]
33 |
34 | "py-3"
35 | [".py-3" {:padding-top "0.75rem"
36 | :padding-bottom "0.75rem"}]
37 |
38 | "pt-1"
39 | [".pt-1" {:padding-top "0.25rem"}]
40 |
41 | "m-40"
42 | [".m-40" {:margin "10rem"}]
43 |
44 | "mx-auto"
45 | [".mx-auto" {:margin-left "auto"
46 | :margin-right "auto"}]
47 |
48 | "-mx-2"
49 | [".-mx-2" {:margin-left "-0.5rem"
50 | :margin-right "-0.5rem"}]
51 |
52 | "space-x-2"
53 | [".space-x-2" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])") {:margin-left "0.5rem"}]]
54 |
55 | "space-y-2"
56 | [".space-y-2" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])") {:margin-top "0.5rem"}]]
57 |
58 | "space-x-2-reverse"
59 | [".space-x-2-reverse" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])") {:margin-right "0.5rem"}]]
60 |
61 | "space-y-2-reverse"
62 | [".space-y-2-reverse" [(garden.selectors.CSSSelector. "&>:not([hidden])~:not([hidden])") {:margin-bottom "0.5rem"}]]))
63 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/svg_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.svg-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "fill-current"
10 | [".fill-current" {:fill "currentColor"}]
11 |
12 | "stroke-current"
13 | [".stroke-current" {:stroke "currentColor"}]
14 |
15 | "stroke-0"
16 | [".stroke-0" {:stroke-width 0}]
17 |
18 | "stroke-3_5"
19 | [".stroke-3_5" {:stroke-width 3.5}]))
20 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/table_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.table-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "border-separate"
10 | [".border-separate" {:border-collapse "separate"}]
11 |
12 | "table-auto"
13 | [".table-auto" {:table-layout "auto"}]))
14 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/transform_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.transform-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
4 |
5 | (deftest component-test
6 | (are [class-name expected-garden]
7 | (= expected-garden (tw-v3-class-name->garden class-name))
8 |
9 | "transform"
10 | [".transform" {:--gi-rotate 0
11 | :--gi-scale-x 1
12 | :--gi-scale-y 1
13 | :--gi-skew-x 0
14 | :--gi-skew-y 0
15 | :--gi-translate-x 0
16 | :--gi-translate-y 0
17 | :transform (str "translateX(var(--gi-translate-x)) "
18 | "translateY(var(--gi-translate-y)) "
19 | "rotate(var(--gi-rotate)) "
20 | "skewX(var(--gi-skew-x)) "
21 | "skewY(var(--gi-skew-y)) "
22 | "scaleX(var(--gi-scale-x)) "
23 | "scaleY(var(--gi-scale-y))")}]
24 |
25 | "transform-none"
26 | [".transform-none" {:transform "none"}]
27 |
28 | "origin-top-right"
29 | [".origin-top-right" {:transform-origin "top right"}]
30 |
31 | "scale-10"
32 | [".scale-10" {:--gi-scale-x 0.1
33 | :--gi-scale-y 0.1}]
34 |
35 | "scale-x-20"
36 | [".scale-x-20" {:--gi-scale-x 0.2}]
37 |
38 | "-scale-y-30"
39 | [".-scale-y-30" {:--gi-scale-y -0.3}]
40 |
41 | "rotate-0"
42 | [".rotate-0" {:--gi-rotate 0}]
43 |
44 | "rotate-45"
45 | [".rotate-45" {:--gi-rotate "45deg"}]
46 |
47 | "-rotate-45"
48 | [".-rotate-45" {:--gi-rotate "-45deg"}]
49 |
50 | "-rotate-2turn"
51 | [".-rotate-2turn" {:--gi-rotate "-2turn"}]
52 |
53 | "translate-x-0"
54 | [".translate-x-0" {:--gi-translate-x 0}]
55 |
56 | "translate-x-1"
57 | [".translate-x-1" {:--gi-translate-x "0.25rem"}]
58 |
59 | "translate-x-1_5"
60 | [".translate-x-1_5" {:--gi-translate-x "0.375rem"}]
61 |
62 | "translate-x-px"
63 | [".translate-x-px" {:--gi-translate-x "1px"}]
64 |
65 | "translate-y-1/2"
66 | [".translate-y-1\\/2" {:--gi-translate-y "50%"}]
67 |
68 | "translate-y-20%"
69 | [".translate-y-20\\%" {:--gi-translate-y "20%"}]
70 |
71 | "translate-y-full"
72 | [".translate-y-full" {:--gi-translate-y "100%"}]
73 |
74 | "skew-x-0"
75 | [".skew-x-0" {:--gi-skew-x 0}]
76 |
77 | "skew-y-10"
78 | [".skew-y-10" {:--gi-skew-y "10deg"}]
79 |
80 | "-skew-y-10"
81 | [".-skew-y-10" {:--gi-skew-y "-10deg"}]
82 |
83 | "-skew-y-0_5rad"
84 | [".-skew-y-0_5rad" {:--gi-skew-y "-0.5rad"}]))
85 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/tw/typography_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.tw.typography-test
2 | (:require [clojure.test :refer [deftest testing is are]]
3 | [girouette.tw.typography :refer [placeholder-pseudo-element]]
4 | [girouette.tw.default-api :refer [tw-v3-class-name->garden]]))
5 |
6 | (deftest component-test
7 | (are [class-name expected-garden]
8 | (= expected-garden (tw-v3-class-name->garden class-name))
9 |
10 | ;; Font family
11 | "font-sans"
12 | [".font-sans" {:font-family "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\""}]
13 |
14 | ;; Font size
15 | "text-sm"
16 | [".text-sm" {:font-size (str 0.875 "rem")
17 | :line-height (str 1.25 "rem")}]
18 |
19 | "text-5xl"
20 | [".text-5xl" {:font-size (str 3 "rem")
21 | :line-height (str 1)}]
22 |
23 | ;; Font size 2
24 | "font-size-10"
25 | [".font-size-10" {:font-size "2.5rem"}]
26 |
27 | "font-size-10%"
28 | [".font-size-10\\%" {:font-size "10%"}]
29 |
30 | "font-size-10vw"
31 | [".font-size-10vw" {:font-size "10vw"}]
32 |
33 | ;; Font smoothing
34 | "antialiased"
35 | [".antialiased" {:-webkit-font-smoothing "antialiased"
36 | :-moz-osx-font-smoothing "grayscale"}]
37 |
38 | ;; Font style
39 | "italic"
40 | [".italic" {:font-style "italic"}]
41 |
42 | ;; Font weight
43 | "font-thin"
44 | [".font-thin" {:font-weight 100}]
45 |
46 | ;; Font variant numeric
47 |
48 | ;; Letter spacing
49 | "tracking-wide"
50 | [".tracking-wide" {:letter-spacing (str 0.025 "em")}]
51 |
52 | ;; Line height
53 | "leading-0"
54 | [".leading-0" {:line-height 0}]
55 |
56 | "leading-3"
57 | [".leading-3" {:line-height (str 0.75 "rem")}]
58 |
59 | "leading-normal"
60 | [".leading-normal" {:line-height 1.5}]
61 |
62 | ;; Line height 2
63 | "line-height-0"
64 | [".line-height-0" {:line-height 0}]
65 |
66 | "line-height-0pt"
67 | [".line-height-0pt" {:line-height "0pt"}]
68 |
69 | "line-height-1_2"
70 | [".line-height-1_2" {:line-height 1.2}]
71 |
72 | "line-height-1/2"
73 | [".line-height-1\\/2" {:line-height "50%"}]
74 |
75 | "line-height-80%"
76 | [".line-height-80\\%" {:line-height "80%"}]
77 |
78 | ;; List style type
79 |
80 | ;; List style position
81 |
82 | ;; Placeholder color
83 | "placeholder-current"
84 | [".placeholder-current" [placeholder-pseudo-element
85 | {:color "currentColor"}]]
86 |
87 | "placeholder-green-300"
88 | [".placeholder-green-300" [placeholder-pseudo-element
89 | {:--gi-placeholder-opacity 1
90 | :color "rgba(134,239,172,var(--gi-placeholder-opacity))"}]]
91 |
92 | "placeholder-green-300-50"
93 | [".placeholder-green-300-50" [placeholder-pseudo-element
94 | {:color "#86efac7f"}]]
95 |
96 | "placeholder-green-300/50"
97 | [".placeholder-green-300\\/50" [placeholder-pseudo-element
98 | {:color "#86efac7f"}]]
99 |
100 | ;; Placeholder opacity
101 | "placeholder-opacity-20"
102 | [".placeholder-opacity-20" {:--gi-placeholder-opacity 0.2}]
103 |
104 | ;; Text align
105 |
106 | ;; Text color
107 | "text-black"
108 | [".text-black" {:--gi-text-opacity 1
109 | :color "rgba(0,0,0,var(--gi-text-opacity))"}]
110 |
111 | "text-current"
112 | [".text-current" {:color "currentColor"}]
113 |
114 | "text-green-300"
115 | [".text-green-300" {:--gi-text-opacity 1
116 | :color "rgba(134,239,172,var(--gi-text-opacity))"}]
117 |
118 | "text-green-300-50"
119 | [".text-green-300-50" {:color "#86efac7f"}]
120 |
121 | "text-#733"
122 | [".text-\\#733" {:--gi-text-opacity 1
123 | :color "rgba(119,51,51,var(--gi-text-opacity))"}]
124 |
125 | "text-#7338"
126 | [".text-\\#7338" {:color "#77333388"}]
127 |
128 | "text-#703030ff"
129 | [".text-\\#703030ff" {:color "#703030ff"}]
130 |
131 | "text-rgb-733"
132 | [".text-rgb-733" {:--gi-text-opacity 1
133 | :color "rgba(119,51,51,var(--gi-text-opacity))"}]
134 |
135 | "text-rgba-7338"
136 | [".text-rgba-7338" {:color "#77333388"}]
137 |
138 | "text-rgba-703030ff"
139 | [".text-rgba-703030ff" {:color "#703030ff"}]
140 |
141 | "text-hsl-0-100-50"
142 | [".text-hsl-0-100-50" {:--gi-text-opacity 1
143 | :color "rgba(255,0,0,var(--gi-text-opacity))"}]
144 |
145 | "text-hsla-0-100-50-50"
146 | [".text-hsla-0-100-50-50" {:color "#ff00007f"}]
147 |
148 | ;; Text opacity
149 | "text-opacity-0"
150 | [".text-opacity-0" {:--gi-text-opacity 0}]
151 |
152 | "text-opacity-5"
153 | [".text-opacity-5" {:--gi-text-opacity 0.05}]
154 |
155 | "text-opacity-100"
156 | [".text-opacity-100" {:--gi-text-opacity 1}]
157 |
158 | ;; Text decoration
159 | "underline"
160 | [".underline" {:text-decoration-line "underline"}]
161 |
162 | ;; Text decoration color
163 | "decoration-inherit"
164 | [".decoration-inherit" {:text-decoration-color "inherit"}]
165 |
166 | "decoration-orange-100"
167 | [".decoration-orange-100" {:text-decoration-color "#ffedd5"}]
168 |
169 | "decoration-#abcdef"
170 | [".decoration-\\#abcdef" {:text-decoration-color "#abcdef"}]
171 |
172 | ;; Text decoration style
173 | "decoration-wavy"
174 | [".decoration-wavy" {:text-decoration-style "wavy"}]
175 |
176 | ;; Text decoration thickness
177 | "decoration-auto"
178 | [".decoration-auto" {:text-decoration-thickness "auto"}]
179 |
180 | "decoration-from-font"
181 | [".decoration-from-font" {:text-decoration-thickness "from-font"}]
182 |
183 | "decoration-3"
184 | [".decoration-3" {:text-decoration-thickness "3px"}]
185 |
186 | "decoration-1rem"
187 | [".decoration-1rem" {:text-decoration-thickness "1rem"}]
188 |
189 | "decoration-1/5"
190 | [".decoration-1\\/5" {:text-decoration-thickness "20%"}]
191 |
192 | ;; Text underline offset
193 | "underline-auto"
194 | [".underline-auto" {:text-underline-offset "auto"}]
195 |
196 | "underline-3"
197 | [".underline-3" {:text-underline-offset "3px"}]
198 |
199 | "underline-1rem"
200 | [".underline-1rem" {:text-underline-offset "1rem"}]
201 |
202 | "underline-1/5"
203 | [".underline-1\\/5" {:text-underline-offset "20%"}]
204 |
205 | ;; Text transform
206 |
207 | ;; Text overflow
208 | "text-clip"
209 | [".text-clip" {:text-overflow "clip"}]
210 |
211 | ;; Text indent
212 | "indent-0"
213 | [".indent-0" {:text-indent "0px"}]
214 |
215 | "indent-1"
216 | [".indent-1" {:text-indent "0.25rem"}]
217 |
218 | "indent-px"
219 | [".indent-px" {:text-indent "1px"}]
220 |
221 | "indent-2em"
222 | [".indent-2em" {:text-indent "2em"}]
223 |
224 | ;; Vertical alignment
225 | "align-sub"
226 | [".align-sub" {:vertical-align "sub"}]
227 |
228 | ;; Whitespace control
229 |
230 | ;; Word break
231 |
232 | ;; Content
233 |
234 | "content-['foo']"
235 | [".content-\\[\\'foo\\'\\]" {:content "\"foo\""}]
236 |
237 | "content-['foo\\'s']"
238 | [".content-\\[\\'foo\\\\\\'s\\'\\]" {:content "\"foo's\""}]
239 |
240 | "content-['foo_bar']"
241 | [".content-\\[\\'foo_bar\\'\\]" {:content "\"foo bar\""}]
242 |
243 | "content-['foo\\_bar']"
244 | [".content-\\[\\'foo\\\\_bar\\'\\]" {:content "\"foo_bar\""}]
245 |
246 | "content-[attr(foo)]"
247 | [".content-\\[attr\\(foo\\)\\]" {:content "attr(foo)"}]))
248 |
--------------------------------------------------------------------------------
/lib/girouette/test/girouette/version_test.cljc:
--------------------------------------------------------------------------------
1 | (ns girouette.version-test
2 | (:require
3 | [clojure.test :refer [deftest testing is are]]
4 | [girouette.version :as gv]))
5 |
6 | (deftest version-<-test
7 | (is (false? (#'gv/version-< [:tw 2] [:tw 1])))
8 | (is (false? (#'gv/version-< [:tw 2] [:tw 1 9])))
9 | (is (false? (#'gv/version-< [:tw 2] [:tw 2])))
10 | (is (false? (#'gv/version-< [:tw 2] [:tw 2 0])))
11 | (is (true? (#'gv/version-< [:tw 2] [:tw 2 0 1])))
12 | (is (true? (#'gv/version-< [:tw 2] [:tw 3]))))
13 |
14 | (deftest version-<=-test
15 | (is (false? (#'gv/version-<= [:tw 2] [:tw 1])))
16 | (is (false? (#'gv/version-<= [:tw 2] [:tw 1 9])))
17 | (is (true? (#'gv/version-<= [:tw 2] [:tw 2])))
18 | (is (true? (#'gv/version-<= [:tw 2] [:tw 2 0])))
19 | (is (true? (#'gv/version-<= [:tw 2] [:tw 2 0 1])))
20 | (is (true? (#'gv/version-<= [:tw 2] [:tw 3]))))
21 |
--------------------------------------------------------------------------------
/lib/girouette/tests.edn:
--------------------------------------------------------------------------------
1 | #kaocha/v1
2 | {:tests [{:id :unit
3 | :type :kaocha.type/clojure.test}
4 | {:id :unit-cljs
5 | :type :kaocha.type/cljs}]}
6 |
--------------------------------------------------------------------------------
/lib/processor/.gitignore:
--------------------------------------------------------------------------------
1 | .cpcache
2 | .idea/
3 | .nrepl-port
4 | *.jar
5 | *.iml
6 | .cljs_node_repl
7 | .clj-kondo
8 | node_modules
9 | out
10 |
11 | *~
12 | [#]*[#]
13 | .\#*
--------------------------------------------------------------------------------
/lib/processor/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src"]
2 |
3 | :deps {org.clojure/clojure {:mvn/version "1.11.1"}
4 | org.clojure/clojurescript {:mvn/version "1.11.60"}
5 | org.clojure/tools.deps.alpha {:mvn/version "0.14.1212"}
6 | org.clojure/tools.analyzer {:mvn/version "1.1.0"}
7 | ;rewrite-clj/rewrite-clj {:mvn/version "0.6.1"}
8 | com.nextjournal/beholder {:mvn/version "1.0.0"}
9 | org.slf4j/slf4j-nop {:mvn/version "1.7.36"}
10 |
11 | garden/garden {:mvn/version "1.3.10"}
12 | ;girouette/girouette {:local/root "../../lib/girouette"}
13 | girouette/girouette {:mvn/version "0.0.8"}}
14 |
15 | :aliases {:test {:extra-paths ["test"]
16 | :extra-deps {lambdaisland/kaocha {:mvn/version "1.68.1059"}
17 | lambdaisland/kaocha-cljs {:mvn/version "1.0.107"}
18 | lambdaisland/kaocha-junit-xml {:mvn/version "0.0.76"}
19 | org.clojure/test.check {:mvn/version "1.1.1"}}}
20 |
21 | ; clojure -M:outdated --upgrade
22 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "1.8.847"}}
23 | :main-opts ["-m" "antq.core"]}
24 |
25 | :depstar {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
26 | :exec-fn hf.depstar/jar
27 | :exec-args {:sync-pom true
28 | :group-id "girouette"
29 | :artifact-id "processor"
30 | :version "0.0.8"
31 | :jar "processor.jar"}}}}
32 | ;; Memo for deploying a new release:
33 | ;; - Change the version above, then build the jar:
34 | ;; clojure -X:depstar
35 | ;; - add a tag "v0.x.y" to the latest commit and push to repo
36 | ;; - deploy:
37 | ;; mvn deploy:deploy-file -Dfile=processor.jar -DpomFile=pom.xml -DrepositoryId=clojars -Durl=https://clojars.org/repo/
38 |
--------------------------------------------------------------------------------
/lib/processor/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | jar
5 | girouette
6 | processor
7 | 0.0.8
8 | processor
9 |
10 | https://github.com/green-coder/girouette
11 | scm:git:git://github.com/green-coder/girouette.git
12 | scm:git:ssh://git@github.com/green-coder/girouette.git
13 | master
14 |
15 |
16 |
17 | org.clojure
18 | clojure
19 | 1.11.1
20 |
21 |
22 | org.clojure
23 | tools.analyzer
24 | 1.1.0
25 |
26 |
27 | org.clojure
28 | clojurescript
29 | 1.11.54
30 |
31 |
32 | com.nextjournal
33 | beholder
34 | 1.0.0
35 |
36 |
37 | org.clojure
38 | tools.deps.alpha
39 | 0.14.1178
40 |
41 |
42 | org.slf4j
43 | slf4j-nop
44 | 1.7.36
45 |
46 |
47 | girouette
48 | girouette
49 | 0.0.8
50 |
51 |
52 | garden
53 | garden
54 | 1.3.10
55 |
56 |
57 |
58 | src
59 |
60 |
61 |
62 | clojars
63 | https://repo.clojars.org/
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/lib/processor/src/girouette/processor/env.clj:
--------------------------------------------------------------------------------
1 | (ns girouette.processor.env)
2 |
3 | ;; Used for storing the configuration data, accessible
4 | ;; from anywhere in the tool, from any thread.
5 | (def config (atom {}))
6 |
--------------------------------------------------------------------------------