├── .github
└── workflows
│ └── go-test.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── banner.jpg
├── examples
├── css-resets
│ ├── README.md
│ ├── main.go
│ └── resets.css
├── full
│ ├── README.md
│ ├── main.go
│ └── styles
│ │ ├── buttons.go
│ │ ├── layout.go
│ │ ├── media.go
│ │ ├── resets.go
│ │ ├── stylesheet.go
│ │ └── theme.go
├── integration-templ
│ ├── README.md
│ ├── components.templ
│ ├── components_templ.go
│ ├── go.mod
│ ├── go.sum
│ ├── home.html
│ ├── home.templ
│ ├── home_templ.go
│ ├── main.go
│ ├── styles.go
│ └── stylesheet.go
├── media-queries
│ ├── main.go
│ └── media.css
├── template-function
│ ├── home.gohtml
│ └── main.go
├── to-file
│ └── main.go
├── to-http-handler
│ └── main.go
└── to-stdout
│ └── main.go
├── go.mod
├── go.sum
├── props
├── align_content.go
├── align_items.go
├── align_self.go
├── appearance.go
├── background_image.go
├── background_position.go
├── background_repeat.go
├── background_size.go
├── border.go
├── border_collapse.go
├── border_style.go
├── box_sizing.go
├── caption_side.go
├── colors.go
├── colors_test.go
├── cursor.go
├── display.go
├── flex_direction.go
├── flex_wrap.go
├── float.go
├── font_family.go
├── font_style.go
├── font_weight.go
├── justify_content.go
├── justify_items.go
├── justify_self.go
├── list_style_position.go
├── list_style_type.go
├── overflow.go
├── position.go
├── print_color_adjust.go
├── text_align.go
├── text_decoration_line.go
├── text_decoration_style.go
├── text_overflow.go
├── text_transform.go
├── text_wrap.go
├── unit.go
├── verticle_align.go
├── visibility.go
└── white_space.go
├── style.go
├── style_test.go
└── variables
├── colors.go
└── sizes.go
/.github/workflows/go-test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Set up Go
14 | uses: actions/setup-go@v5
15 | with:
16 | go-version: '>=1.22.0'
17 |
18 | - name: Check out code
19 | uses: actions/checkout@v4
20 |
21 | - name: Install dependencies
22 | run: |
23 | go mod tidy
24 |
25 | - name: Run vet
26 | run: |
27 | go vet ./...
28 |
29 | - name: Run tests
30 | run: |
31 | go test ./...
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | public
4 | resources
5 | tmp
6 |
7 | .hugo_build.lock
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ### v0.1.2
6 |
7 | - Move the `Props` CSS writing into a `Props.CSS` func, so it can be used in isolation of the `Style` struct. This allows for the `Props` CSS to be used in other contexts, such as in a `style` attribute in HTML.
8 | - Don't write empty values in the CSS string. eg: `.test{}` will not be written if there are no props to write.
9 |
10 | ### v0.1.1
11 |
12 | Added new props:
13 |
14 | - align-content
15 | - align-self
16 | - border-collapse
17 |
18 | Expanded props:
19 |
20 | - justify-content
21 | - justify-items
22 | - justify-self
23 |
24 | ### v0.1.0
25 |
26 | - Allow a color to be mixed with another color.
27 | - Change `props.Color` to allow any type of golang's `color.Color` interface.
28 |
29 | ### v0.0.9
30 |
31 | Added new variables:
32 |
33 | - extra sizes
34 |
35 | ### v0.0.8
36 |
37 | Added new variables:
38 |
39 | - colors
40 | - sizes
41 |
42 | Added new props:
43 |
44 | - max-height
45 | - max-width
46 |
47 | Added new units:
48 |
49 | - vh
50 | - vw
51 |
52 | ### v0.0.7
53 |
54 | Added new props:
55 |
56 | - font-family (basic defaults from tailwindcss)
57 | - text-decoration-color
58 | - text-decoration-line
59 | - text-decoration-style
60 | - text-decoration-thickness
61 | - text-indent
62 | - text-transform
63 | - text-underline-offset
64 | - text-wrap
65 |
66 | ### v0.0.6
67 |
68 | Added new props:
69 |
70 | - column-gap
71 | - row-gap
72 | - box-sizing
73 | - list-style-position
74 | - list-style-type
75 | - vertical-align
76 | - z-index
77 | - visibility
78 | - opacity
79 |
80 | ### v0.0.5
81 |
82 | Added explicit CustomProp type and make CustomProps a slice to preserve ordering.
83 |
84 | ### v0.0.4
85 |
86 | Added support for custom props.
87 |
88 | Added new props:
89 |
90 | - cursor
91 | - border-bottom-left-radius
92 | - border-bottom-right-radius
93 | - border-top-left-radius
94 | - border-top-right-radius
95 |
96 | ### v0.0.3
97 |
98 | Added new props:
99 |
100 | - bottom
101 | - left
102 | - right
103 | - top
104 | - gap
105 | - text-overflow
106 | - white-space
107 |
108 | ### v0.0.2
109 |
110 | Added licensing information.
111 |
112 | ### v0.0.1
113 |
114 | Initial release.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Accent Design Group Ltd.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/AccentDesign/gcss/actions/workflows/go-test.yml)
2 | [](https://goreportcard.com/report/github.com/AccentDesign/gcss)
3 |
4 |
5 |
6 |
7 | # gcss
8 |
9 | CSS written in Pure Go.
10 |
11 | ## Motivation
12 |
13 | This is really just a bit of fun and a way to write CSS in Go. I wanted to see if it was possible and what would it look like.
14 | I wanted to find a way to easily control the CSS from the server side and not have to worry about pre-building the css to take variables and stuff.
15 | I didnt want to use UI libraries that are written for JS frameworks and I didn't want to use preprocessors or linters that add more steps to the build process.
16 |
17 | Could I just use CSS? Yes of course and I will, but I wanted to see if I could write CSS in Go as this is what is compiling the rest of the project.
18 |
19 | ## Gopher
20 |
21 | No it looks nothing like the Go gopher, but it's a gopher and I like it. It's the best I could get from the LM without giving up, [ideogram.ai (1400097641)](https://ideogram.ai/g/E-5MQp7QTPO4uyF9PvERzw/3).
22 | ## Next steps
23 |
24 | The next steps for this project are to add more features to the CSS package.
25 | This includes adding support for more CSS properties when the need arises.
26 | What I don't want to do is to add support for all CSS functionality as some things are better in CSS, but I do want to be able to create
27 | a few UI components that are configurable using Go.
28 |
29 | ## Installation
30 |
31 | ```bash
32 | go get github.com/AccentDesign/gcss
33 | ```
34 |
35 | ## Quickstart
36 |
37 | There is a separate repo with the full example [here](https://github.com/AccentDesign/gcss-starter) which will evolve over time.
38 |
39 | ```bash
40 | git clone https://github.com/AccentDesign/gcss-starter.git
41 | ```
42 |
43 | install the dependencies:
44 |
45 | ```bash
46 | # for hot reloading
47 | go install github.com/cosmtrek/air@latest
48 | ```
49 |
50 | ```bash
51 | go mod tidy
52 | ```
53 |
54 | run the server:
55 |
56 | ```bash
57 | air
58 | ```
59 |
60 | ## Usage
61 |
62 | ### Basic usage
63 |
64 | `gcss` defines a `Style` type that can be used to hold the properties for a specific css selector, eg:
65 |
66 | ```go
67 | style := gcss.Style{
68 | Selector: "body",
69 | Props: gcss.Props{
70 | BackgroundColor: props.ColorRGBA(0, 0, 0, 128),
71 | },
72 | }
73 | ```
74 |
75 | The `CSS` function on the `Style` is used to write the style to a `io.Writer`:
76 |
77 | ```go
78 | style.CSS(os.Stdout)
79 | ```
80 |
81 | which gives you:
82 |
83 | ```css
84 | body{background-color:rgba(0,0,0,0.50);}
85 | ```
86 |
87 | That's all there is to it. But it's not very useful on it's own I hear you say.
88 |
89 | ### Multiple styles
90 |
91 | Well you can then use that to define a `Styles` type that can be used to hold multiple `Style` types:
92 |
93 | ```go
94 | type Styles []gcss.Style
95 |
96 | func (s Styles) CSS(w io.Writer) error {
97 | // handle your errors
98 | for _, style := range s {
99 | style.CSS(w)
100 | }
101 | return nil
102 | }
103 |
104 | styles := Styles{
105 | {
106 | Selector: "body",
107 | Props: gcss.Props{
108 | BackgroundColor: props.ColorRGBA(0, 0, 0, 128),
109 | },
110 | },
111 | {
112 | Selector: "main",
113 | Props: gcss.Props{
114 | Padding: props.UnitRem(8.5),
115 | },
116 | },
117 | }
118 |
119 | styles.CSS(os.Stdout)
120 | ```
121 |
122 | which gives you:
123 |
124 | ```css
125 | /* formatted for visibility */
126 | body{
127 | background-color:rgba(0,0,0,0.50);
128 | }
129 | main{
130 | padding:8.500rem;
131 | }
132 | ```
133 |
134 | ### Need a bit more? what about a dark and light theme? keep the last example in mind and read on.
135 |
136 | Define a `Theme` type that can be used to hold attributes for a specific theme, eg:
137 |
138 | ```go
139 | type Theme struct {
140 | MediaQuery string
141 | Background props.Color
142 | }
143 |
144 | func (t *Theme) CSS(w io.Writer) error {
145 | // handle your errors
146 | fmt.Fprintf(w, "%s{", t.MediaQuery)
147 | for _, style := range t.Styles() {
148 | style.CSS(w)
149 | }
150 | fmt.Fprint(w, "}")
151 | }
152 |
153 | // Styles returns the styles for the theme.
154 | // Can be any number of styles you want and any number of functions
155 | // you just need them in the CSS function to loop over.
156 | func (t *Theme) Styles() Styles {
157 | return Styles{
158 | {
159 | Selector: "body",
160 | Props: gcss.Props{
161 | BackgroundColor: t.Background,
162 | },
163 | },
164 | }
165 | }
166 | ```
167 |
168 | Then you can define a `Stylesheet` type that can be used to hold multiple `Theme` types:
169 |
170 | ```go
171 | type Stylesheet struct {
172 | Dark *Theme
173 | Light *Theme
174 | }
175 |
176 | func (s *Stylesheet) CSS(w io.Writer) error {
177 | // handle your errors
178 | s.Dark.CSS(w)
179 | s.Light.CSS(w)
180 | return nil
181 | }
182 | ```
183 |
184 | Finally, you can use the `Stylesheet` type to write the css to a `io.Writer`:
185 |
186 | ```go
187 | styles := Stylesheet{
188 | Dark: &Theme{
189 | MediaQuery: "@media (prefers-color-scheme: dark)",
190 | Background: props.ColorRGBA(0, 0, 0, 255),
191 | },
192 | Light: &Theme{
193 | MediaQuery: "@media (prefers-color-scheme: light)",
194 | Background: props.ColorRGBA(255, 255, 255, 255),
195 | },
196 | }
197 |
198 | styles.CSS(os.Stdout)
199 | ```
200 |
201 | gives you:
202 |
203 | ```css
204 | /* formatted for visibility */
205 | @media (prefers-color-scheme: dark) {
206 | body{
207 | background-color:rgba(0,0,0,1.00);
208 | }
209 | }
210 | @media (prefers-color-scheme: light) {
211 | body{
212 | background-color:rgba(255,255,255,1.00);
213 | }
214 | }
215 | ```
216 |
217 | Hopefully this will get you going. The rest is up to you.
218 |
219 | * Maybe create a button function that takes a `props.Color` and returns a Style.
220 | * Or add extra `Styles` to the `Stylesheet` to additionally include non themed styles.
221 | * It's all about how you construct the `Stylesheet` and use the `gcss.Style` type.
222 | * If I could have created a `Stylesheet` type that fits well any use case at all I would have, but there is a world of possibility, so I left it up to you.
223 |
224 | ## The benefits
225 |
226 | * Total control of the CSS from the server side.
227 | * CSS doesn't have mixins, but you can create a function that returns a `Style` type and reuse it.
228 | * Keeps the css free of variables.
229 | * Keeps html free of classes like `bg-gray-50 text-black dark:bg-slate-800 dark:text-white` and eliminates the need to remember to add the dark variant.
230 | * I recently saw a button component on an html page 10 times with over 1800 characters in the class attribute of each. This is not maintainable nor debuggable.
231 | * Keeps the css clean and easy to debug with no overrides like the above.
232 |
233 | ## Examples
234 |
235 | For example usage see the [examples](./examples) directory that include:
236 |
237 | * [Full example](./examples/full) - A full example including base, media and themed styles with a `sync.Mutex` for caching the css in a `http.HandleFunc`.
238 | * [CSS resets](./examples/css-resets) - A simple example collection of css resets.
239 | * [Templ integration](./examples/integration-templ) - An example of how to load styles from gcss with the [templ](https://templ.guide) package.
240 | * [Media queries](./examples/media-queries) - An example of how to use media queries.
241 | * [Template function](./examples/template-function) - An example of how to write css to the template directly via `template.FuncMap` and `template.CSS`.
242 | * [Write to a file](./examples/to-file) - An example of how to write to a file.
243 | * [Write to an HTTP handler](./examples/to-http-handler) - An example of how to write to an http handler.
244 | * [Write to stdout](./examples/to-stdout) - An example of how to write to stdout.
245 |
246 | ## Contributing
247 |
248 | If you would like to contribute to this project, please open an issue or a pull request. We welcome all contributions and ideas.
249 |
250 | ## Mix it up with other CSS frameworks
251 |
252 | You can mix `gcss` with other CSS frameworks like `tailwindcss` for example:
253 |
254 | separate the css files into base and utils:
255 |
256 | ```css
257 | /* base.css */
258 | @tailwind base;
259 | ```
260 |
261 | ```css
262 | /* utils.css */
263 | @tailwind utilities;
264 | ```
265 |
266 | Then add the `gcss` styles in between in your html:
267 |
268 | ```html
269 |
270 |
271 |
272 | ```
273 |
274 | Try to keep the specificity of the `gcss` styles to 1 by using single classes this will ensure any `tailwindcss` utilities
275 | will be able to overwrite your styles where required.
276 |
--------------------------------------------------------------------------------
/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AccentDesign/gcss/3b5aa22d0680d512ed6f3d2ae7807fddd80d9610/banner.jpg
--------------------------------------------------------------------------------
/examples/css-resets/README.md:
--------------------------------------------------------------------------------
1 | # CSS Resets
2 |
3 | A useful collection of CSS resets to start your projects with a clean slate.
4 |
--------------------------------------------------------------------------------
/examples/css-resets/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | "os"
8 | )
9 |
10 | var resets = []gcss.Style{
11 | {
12 | Selector: "*,::after,::before,::backdrop,::file-selector-button",
13 | Props: gcss.Props{
14 | Border: props.Border{
15 | Width: variables.Size0,
16 | Style: props.BorderStyleSolid,
17 | },
18 | BoxSizing: props.BoxSizingBorderBox,
19 | Margin: variables.Size0,
20 | Padding: variables.Size0,
21 | },
22 | },
23 | {
24 | Selector: "html,:host",
25 | Props: gcss.Props{
26 | FontFamily: props.FontFamilySans,
27 | LineHeight: props.UnitRaw(1.5),
28 | },
29 | CustomProps: []gcss.CustomProp{
30 | {Attr: "-webkit-text-size-adjust", Value: "100%"},
31 | {Attr: "tab-size", Value: "4"},
32 | },
33 | },
34 | {
35 | Selector: "body",
36 | Props: gcss.Props{
37 | LineHeight: props.UnitInherit(),
38 | },
39 | },
40 | {
41 | Selector: "hr",
42 | Props: gcss.Props{
43 | Color: props.ColorInherit(),
44 | Height: variables.Size0,
45 | },
46 | CustomProps: []gcss.CustomProp{
47 | {Attr: "border-top-width", Value: "1px"},
48 | },
49 | },
50 | {
51 | Selector: "abbr:where([title])",
52 | Props: gcss.Props{
53 | TextDecorationLine: props.TextDecorationLineUnderline,
54 | TextDecorationStyle: props.TextDecorationStyleDotted,
55 | },
56 | },
57 | {
58 | Selector: "h1,h2,h3,h4,h5,h6",
59 | Props: gcss.Props{
60 | FontSize: props.UnitInherit(),
61 | FontWeight: "inherit",
62 | },
63 | },
64 | {
65 | Selector: "a",
66 | Props: gcss.Props{
67 | Color: props.ColorInherit(),
68 | TextDecorationLine: "inherit",
69 | TextDecorationStyle: "inherit",
70 | },
71 | },
72 | {
73 | Selector: "b,strong",
74 | Props: gcss.Props{
75 | FontWeight: "bolder",
76 | },
77 | },
78 | {
79 | Selector: "code,kbd,samp,pre",
80 | Props: gcss.Props{
81 | FontFamily: props.FontFamilyMono,
82 | FontSize: props.UnitEm(1),
83 | },
84 | },
85 | {
86 | Selector: "small",
87 | Props: gcss.Props{
88 | FontSize: props.UnitPercent(80),
89 | },
90 | },
91 | {
92 | Selector: "sub,sup",
93 | Props: gcss.Props{
94 | FontSize: props.UnitPercent(75),
95 | LineHeight: variables.Size0,
96 | Position: props.PositionRelative,
97 | VerticalAlign: props.VerticalAlignBaseline,
98 | },
99 | },
100 | {
101 | Selector: "sub",
102 | Props: gcss.Props{
103 | Bottom: props.UnitEm(-0.25),
104 | },
105 | },
106 | {
107 | Selector: "sup",
108 | Props: gcss.Props{
109 | Top: props.UnitEm(-0.5),
110 | },
111 | },
112 | {
113 | Selector: "table",
114 | Props: gcss.Props{
115 | BorderCollapse: props.BorderCollapseCollapse,
116 | BorderColor: props.ColorInherit(),
117 | TextIndent: variables.Size0,
118 | },
119 | },
120 | {
121 | Selector: "button,input,optgroup,select,textarea,::file-selector-button",
122 | Props: gcss.Props{
123 | BackgroundColor: props.ColorTransparent(),
124 | Color: props.ColorInherit(),
125 | FontFamily: "inherit",
126 | FontSize: props.UnitInherit(),
127 | },
128 | CustomProps: []gcss.CustomProp{
129 | {Attr: "letter-spacing", Value: "inherit"},
130 | },
131 | },
132 | {
133 | Selector: "input:where(:not([type='button'],[type='reset'],[type='submit'])),select,textarea",
134 | Props: gcss.Props{
135 | Border: props.Border{
136 | Width: props.UnitPx(1),
137 | Style: props.BorderStyleSolid,
138 | },
139 | },
140 | },
141 | {
142 | Selector: "button,input:where([type='button'],[type='reset'],[type='submit']),::file-selector-button",
143 | Props: gcss.Props{
144 | Appearance: props.AppearanceButton,
145 | },
146 | },
147 | {
148 | Selector: ":-moz-focusring",
149 | CustomProps: []gcss.CustomProp{
150 | {Attr: "outline", Value: "auto"},
151 | },
152 | },
153 | {
154 | Selector: ":-moz-ui-invalid",
155 | CustomProps: []gcss.CustomProp{
156 | {Attr: "box-shadow", Value: "none"},
157 | },
158 | },
159 | {
160 | Selector: "progress",
161 | Props: gcss.Props{
162 | VerticalAlign: props.VerticalAlignBaseline,
163 | },
164 | },
165 | {
166 | Selector: "::-webkit-inner-spin-button,::-webkit-outer-spin-button",
167 | Props: gcss.Props{
168 | Height: props.UnitAuto(),
169 | },
170 | },
171 | {
172 | Selector: "::-webkit-search-decoration",
173 | CustomProps: []gcss.CustomProp{
174 | {Attr: "-webkit-appearance", Value: "none"},
175 | },
176 | },
177 | {
178 | Selector: "summary",
179 | Props: gcss.Props{
180 | Display: props.DisplayListItem,
181 | },
182 | },
183 | {
184 | Selector: "ol,ul,menu",
185 | Props: gcss.Props{
186 | ListStyleType: props.ListStyleTypeNone,
187 | ListStylePosition: props.ListStylePositionInside,
188 | },
189 | },
190 | {
191 | Selector: "textarea",
192 | CustomProps: []gcss.CustomProp{
193 | {Attr: "resize", Value: "vertical"},
194 | },
195 | },
196 | {
197 | Selector: "::placeholder",
198 | Props: gcss.Props{
199 | Color: props.Color{Keyword: "color-mix(in srgb, currentColor 50%, transparent)"},
200 | Opacity: props.UnitRaw(1),
201 | },
202 | },
203 | {
204 | Selector: ":disabled",
205 | Props: gcss.Props{
206 | Cursor: props.CursorDefault,
207 | },
208 | },
209 | {
210 | Selector: "img,svg,video,canvas,audio,iframe,embed,object",
211 | Props: gcss.Props{
212 | Display: props.DisplayBlock,
213 | VerticalAlign: props.VerticalAlignMiddle,
214 | },
215 | },
216 | {
217 | Selector: "img,video",
218 | Props: gcss.Props{
219 | Height: props.UnitAuto(),
220 | MaxWidth: variables.Full,
221 | },
222 | },
223 | {
224 | Selector: "[hidden]",
225 | Props: gcss.Props{
226 | Display: props.DisplayNone,
227 | },
228 | },
229 | }
230 |
231 | func main() {
232 | file, err := os.Create("resets.css")
233 | if err != nil {
234 | panic(err)
235 | }
236 | defer file.Close()
237 |
238 | for _, style := range resets {
239 | if err := style.CSS(file); err != nil {
240 | panic(err)
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/examples/css-resets/resets.css:
--------------------------------------------------------------------------------
1 | *,::after,::before,::backdrop,::file-selector-button{border:0 solid;box-sizing:border-box;margin:0;padding:0;}html,:host{font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";line-height:1.5;-webkit-text-size-adjust:100%;tab-size:4;}body{line-height:inherit;}hr{color:inherit;height:0;border-top-width:1px;}abbr:where([title]){text-decoration-line:underline;text-decoration-style:dotted;}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit;}a{color:inherit;text-decoration-line:inherit;text-decoration-style:inherit;}b,strong{font-weight:bolder;}code,kbd,samp,pre{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:1.000em;}small{font-size:80.00%;}sub,sup{font-size:75.00%;line-height:0;position:relative;vertical-align:baseline;}sub{bottom:-0.250em;}sup{top:-0.500em;}table{border-collapse:collapse;border-color:inherit;text-indent:0;}button,input,optgroup,select,textarea,::file-selector-button{background-color:transparent;color:inherit;font-family:inherit;font-size:inherit;letter-spacing:inherit;}input:where(:not([type='button'],[type='reset'],[type='submit'])),select,textarea{border:1px solid;}button,input:where([type='button'],[type='reset'],[type='submit']),::file-selector-button{appearance:button;}:-moz-focusring{outline:auto;}:-moz-ui-invalid{box-shadow:none;}progress{vertical-align:baseline;}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto;}::-webkit-search-decoration{-webkit-appearance:none;}summary{display:list-item;}ol,ul,menu{list-style-position:inside;list-style-type:none;}textarea{resize:vertical;}::placeholder{color:color-mix(in srgb, currentColor 50%, transparent);opacity:1;}:disabled{cursor:default;}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle;}img,video{height:auto;max-width:100.00%;}[hidden]{display:none;}
--------------------------------------------------------------------------------
/examples/full/README.md:
--------------------------------------------------------------------------------
1 | # Full example with mutex
2 |
3 | This example hase the following:
4 |
5 | * A `StyleSheet` that has has global resets, base styles, media queries for devices, themes and a `Mutex` that generates the CSS only once to avoid multiple builds.
6 | * The `main` element has base styles and media queries for different screen sizes.
7 | * Both `body` and `buttons` have base styles as well as themed for the likes of background and foreground variations.
8 |
9 | ## Usage
10 |
11 | For an example of adding form styles.
12 |
13 | 1. Create a new `form.go` file. and add the following code (or add what you need):
14 |
15 | ```go
16 | // Form returns the styles for buttons for the base stylesheet.
17 | func (ss *StyleSheet) Form() Styles {
18 | return Styles{}
19 | }
20 |
21 | // Form returns the styles for the layout for the media.
22 | func (m *Media) Form() Styles {
23 | return Styles{}
24 | }
25 |
26 | // Form returns the styles for the layout for the theme.
27 | func (t *Theme) Form() Styles {
28 | return Styles{}
29 | }
30 | ```
31 |
32 | 2. Then add the functions to the `CSS` function in `StyleSheet`, `Media` and `Theme`.
33 |
34 |
35 | ## HTML
36 |
37 | ```html
38 |
39 |
40 |
41 |
42 |
43 |
44 | Theme
45 |
46 |
47 |
48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
50 |
51 | Click me
52 |
53 |
54 |
55 |
56 | ```
57 |
58 | ## CSS
59 |
60 | ```css
61 | /* formatted for visual clarity */
62 |
63 | /* resets */
64 | *, ::after, ::before, ::backdrop, ::file-selector-button {
65 | border: 0 solid;
66 | box-sizing: border-box;
67 | margin: 0;
68 | padding: 0;
69 | }
70 |
71 | html, :host {
72 | font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
73 | line-height: 1.5;
74 | -webkit-text-size-adjust: 100%;
75 | tab-size: 4;
76 | }
77 |
78 | body {
79 | line-height: inherit;
80 | }
81 |
82 | /* base */
83 | body {
84 | min-height: 100vh;
85 | }
86 |
87 | main {
88 | display: grid;
89 | }
90 |
91 | .button {
92 | align-items: center;
93 | border-radius: 0.375rem;
94 | display: inline-flex;
95 | font-size: 0.875rem;
96 | font-weight: 500;
97 | height: 2.500rem;
98 | justify-content: center;
99 | line-height: 1.250rem;
100 | padding-bottom: 0.500rem;
101 | padding-left: 1.000rem;
102 | padding-right: 1.000rem;
103 | padding-top: 0.500rem;
104 | }
105 |
106 | /* media */
107 | @media (max-width: 768px) {
108 | main {
109 | padding: 2.000rem;
110 | row-gap: 1.500rem;
111 | }
112 | }
113 |
114 | @media (min-width: 769px) {
115 | main {
116 | padding: 4.000rem;
117 | row-gap: 2.000rem;
118 | }
119 | }
120 |
121 | /* themes */
122 | @media (prefers-color-scheme: light) {
123 | body {
124 | background-color: rgba(255, 255, 255, 1.00);
125 | color: rgba(23, 23, 23, 1.00);
126 | }
127 |
128 | .button-primary {
129 | background-color: rgba(23, 23, 23, 1.00);
130 | color: rgba(255, 255, 255, 1.00);
131 | }
132 | }
133 |
134 | @media (prefers-color-scheme: dark) {
135 | body {
136 | background-color: rgba(23, 23, 23, 1.00);
137 | color: rgba(245, 245, 245, 1.00);
138 | }
139 |
140 | .button-primary {
141 | background-color: rgba(255, 255, 255, 1.00);
142 | color: rgba(23, 23, 23, 1.00);
143 | }
144 | }
145 |
146 | ```
--------------------------------------------------------------------------------
/examples/full/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/AccentDesign/gcss/examples/full/styles"
6 | "net/http"
7 | )
8 |
9 | var (
10 | stylesheet = styles.NewStyleSheet()
11 | html = `
12 |
13 |
14 |
15 |
16 |
17 |
18 | Theme
19 |
20 |
21 |
22 |
23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
24 |
25 | Click me
26 |
27 |
28 |
29 |
30 | `
31 | )
32 |
33 | func main() {
34 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
35 | if _, err := fmt.Fprint(w, html); err != nil {
36 | http.Error(w, err.Error(), http.StatusInternalServerError)
37 | }
38 | })
39 |
40 | http.HandleFunc("/stylesheet.css", func(w http.ResponseWriter, r *http.Request) {
41 | w.Header().Set("Content-Type", "text/css")
42 | if err := stylesheet.CSS(w); err != nil {
43 | http.Error(w, err.Error(), http.StatusInternalServerError)
44 | }
45 | })
46 |
47 | fmt.Println("Server started at http://localhost:8080")
48 |
49 | if err := http.ListenAndServe(":8080", nil); err != nil {
50 | panic(err)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/full/styles/buttons.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | )
8 |
9 | // Buttons returns the styles for buttons for the base stylesheet.
10 | func (ss *StyleSheet) Buttons() Styles {
11 | return Styles{
12 | {
13 | Selector: ".button",
14 | Props: gcss.Props{
15 | AlignItems: props.AlignItemsCenter,
16 | BorderRadius: variables.Size1H,
17 | Display: props.DisplayInlineFlex,
18 | FontSize: variables.Size3H,
19 | FontWeight: props.FontWeightMedium,
20 | Height: variables.Size10,
21 | JustifyContent: props.JustifyContentCenter,
22 | LineHeight: variables.Size5,
23 | PaddingTop: variables.Size2,
24 | PaddingRight: variables.Size4,
25 | PaddingBottom: variables.Size2,
26 | PaddingLeft: variables.Size4,
27 | },
28 | },
29 | }
30 | }
31 |
32 | // Buttons returns the styles for buttons for the theme.
33 | func (t *Theme) Buttons() Styles {
34 | return Styles{
35 | {
36 | Selector: ".button-primary",
37 | Props: gcss.Props{
38 | BackgroundColor: t.Primary,
39 | Color: t.PrimaryForeground,
40 | },
41 | },
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/full/styles/layout.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | )
8 |
9 | // Layout returns the styles for the layout for the base stylesheet.
10 | func (ss *StyleSheet) Layout() Styles {
11 | return Styles{
12 | {
13 | Selector: "body",
14 | Props: gcss.Props{
15 | MinHeight: variables.FullScreenHeight,
16 | },
17 | },
18 | {
19 | Selector: "main",
20 | Props: gcss.Props{
21 | Display: props.DisplayGrid,
22 | },
23 | },
24 | }
25 | }
26 |
27 | // Layout returns the styles for the layout for the media.
28 | func (m *Media) Layout() Styles {
29 | return Styles{
30 | {
31 | Selector: "main",
32 | Props: gcss.Props{
33 | Padding: m.Padding,
34 | RowGap: m.VerticalGap,
35 | },
36 | },
37 | }
38 | }
39 |
40 | // Layout returns the styles for the layout for the theme.
41 | func (t *Theme) Layout() Styles {
42 | return Styles{
43 | {
44 | Selector: "body",
45 | Props: gcss.Props{
46 | BackgroundColor: t.Background,
47 | Color: t.Foreground,
48 | },
49 | },
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/full/styles/media.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/AccentDesign/gcss/props"
7 | "github.com/AccentDesign/gcss/variables"
8 | "io"
9 | "slices"
10 | )
11 |
12 | type (
13 | Media struct {
14 | Query string
15 | Padding props.Unit
16 | VerticalGap props.Unit
17 | }
18 | )
19 |
20 | var (
21 | mobileMedia = &Media{
22 | Query: "@media (max-width: 768px)",
23 | Padding: variables.Size8,
24 | VerticalGap: variables.Size6,
25 | }
26 | desktopMedia = &Media{
27 | Query: "@media (min-width: 769px)",
28 | Padding: variables.Size16,
29 | VerticalGap: variables.Size8,
30 | }
31 | )
32 |
33 | // CSS Writes the CSS for the media to the writer.
34 | func (m *Media) CSS(w io.Writer) error {
35 | var buf bytes.Buffer
36 | for _, style := range slices.Concat(
37 | m.Layout(),
38 | ) {
39 | if err := style.CSS(&buf); err != nil {
40 | return err
41 | }
42 | }
43 | if buf.Len() > 0 {
44 | _, err := fmt.Fprintf(w, "%s{%s}", m.Query, buf.String())
45 | return err
46 | }
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/examples/full/styles/resets.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | )
8 |
9 | // Resets returns the styles for resets for the base stylesheet.
10 | func (ss *StyleSheet) Resets() Styles {
11 | return Styles{
12 | {
13 | Selector: "*,::after,::before,::backdrop,::file-selector-button",
14 | Props: gcss.Props{
15 | Border: props.Border{
16 | Width: variables.Size0,
17 | Style: props.BorderStyleSolid,
18 | },
19 | BoxSizing: props.BoxSizingBorderBox,
20 | Margin: variables.Size0,
21 | Padding: variables.Size0,
22 | },
23 | },
24 | {
25 | Selector: "html,:host",
26 | Props: gcss.Props{
27 | FontFamily: props.FontFamilySans,
28 | LineHeight: props.UnitRaw(1.5),
29 | },
30 | CustomProps: []gcss.CustomProp{
31 | {Attr: "-webkit-text-size-adjust", Value: "100%"},
32 | {Attr: "tab-size", Value: "4"},
33 | },
34 | },
35 | {
36 | Selector: "body",
37 | Props: gcss.Props{
38 | LineHeight: props.UnitInherit(),
39 | },
40 | },
41 | // more resets
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/full/styles/stylesheet.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "bytes"
5 | "github.com/AccentDesign/gcss"
6 | "io"
7 | "slices"
8 | "sync"
9 | )
10 |
11 | type (
12 | Styles []gcss.Style
13 | StyleSheet struct {
14 | Media []*Media
15 | Themes []*Theme
16 | css bytes.Buffer
17 | mutex sync.Mutex
18 | }
19 | )
20 |
21 | // CSS Writes the CSS for the stylesheet to the writer.
22 | func (ss *StyleSheet) CSS(w io.Writer) error {
23 | ss.mutex.Lock()
24 | defer ss.mutex.Unlock()
25 |
26 | if ss.css.Len() > 0 {
27 | _, err := w.Write(ss.css.Bytes())
28 | return err
29 | }
30 |
31 | for _, style := range slices.Concat(
32 | ss.Resets(),
33 | ss.Layout(),
34 | ss.Buttons(),
35 | ) {
36 | if err := style.CSS(&ss.css); err != nil {
37 | return err
38 | }
39 | }
40 |
41 | for _, media := range ss.Media {
42 | if err := media.CSS(&ss.css); err != nil {
43 | return err
44 | }
45 | }
46 |
47 | for _, theme := range ss.Themes {
48 | if err := theme.CSS(&ss.css); err != nil {
49 | return err
50 | }
51 | }
52 |
53 | _, err := w.Write(ss.css.Bytes())
54 | return err
55 | }
56 |
57 | // NewStyleSheet returns a new stylesheet. It includes the media queries and themes.
58 | func NewStyleSheet() *StyleSheet {
59 | return &StyleSheet{
60 | Media: []*Media{mobileMedia, desktopMedia},
61 | Themes: []*Theme{lightTheme, darkTheme},
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/examples/full/styles/theme.go:
--------------------------------------------------------------------------------
1 | package styles
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/AccentDesign/gcss/props"
7 | "github.com/AccentDesign/gcss/variables"
8 | "io"
9 | "slices"
10 | )
11 |
12 | type (
13 | Theme struct {
14 | MediaQuery string
15 | Background props.Color
16 | Foreground props.Color
17 | Primary props.Color
18 | PrimaryForeground props.Color
19 | }
20 | )
21 |
22 | var (
23 | lightTheme = &Theme{
24 | MediaQuery: "@media (prefers-color-scheme: light)",
25 | Background: variables.White,
26 | Foreground: variables.Neutral900,
27 | Primary: variables.Neutral900,
28 | PrimaryForeground: variables.White,
29 | }
30 | darkTheme = &Theme{
31 | MediaQuery: "@media (prefers-color-scheme: dark)",
32 | Background: variables.Neutral900,
33 | Foreground: variables.Neutral100,
34 | Primary: variables.White,
35 | PrimaryForeground: variables.Neutral900,
36 | }
37 | )
38 |
39 | // CSS Writes the CSS for the theme to the writer.
40 | func (t *Theme) CSS(w io.Writer) error {
41 | var buf bytes.Buffer
42 | for _, style := range slices.Concat(
43 | t.Layout(),
44 | t.Buttons(),
45 | ) {
46 | if err := style.CSS(&buf); err != nil {
47 | return err
48 | }
49 | }
50 | if buf.Len() > 0 {
51 | _, err := fmt.Fprintf(w, "%s{%s}", t.MediaQuery, buf.String())
52 | return err
53 | }
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/examples/integration-templ/README.md:
--------------------------------------------------------------------------------
1 | # Templ Integration
2 |
3 | This is an example of how to integrate the awesome [Templ](https://templ.guide) with `gcss`.
4 | It's on the basic side, but it should give you a good idea of how to get started.
5 |
6 | I will include a better example in the future, but for now, this should be enough to get you started.
7 |
8 | ## Notes
9 |
10 | The Stylesheet type is there till we decide if it will be in the package itself or not.
11 | Though this is unlikely to change till there is a clear need for it.
12 |
13 | When you render the template, you will see that it renders two buttons that share the same style.
14 | This is done through the usage of the [Once](https://templ.guide/syntax-and-usage/render-once) handler provided by templ
15 | and allows you to selectively render styles when they are required.
16 |
--------------------------------------------------------------------------------
/examples/integration-templ/components.templ:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | templ ButtonCSS() {
4 | @buttonStylesHandle.Once() {
5 | @buttonStyles
6 | }
7 | }
8 |
9 | templ Button(name string) {
10 | @ButtonCSS()
11 | {name}
12 | }
13 |
--------------------------------------------------------------------------------
/examples/integration-templ/components_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.707
4 | package main
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func ButtonCSS() templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
27 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
28 | if !templ_7745c5c3_IsBuffer {
29 | templ_7745c5c3_Buffer = templ.GetBuffer()
30 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
31 | }
32 | templ_7745c5c3_Err = buttonStyles.Render(ctx, templ_7745c5c3_Buffer)
33 | if templ_7745c5c3_Err != nil {
34 | return templ_7745c5c3_Err
35 | }
36 | if !templ_7745c5c3_IsBuffer {
37 | _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
38 | }
39 | return templ_7745c5c3_Err
40 | })
41 | templ_7745c5c3_Err = buttonStylesHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
42 | if templ_7745c5c3_Err != nil {
43 | return templ_7745c5c3_Err
44 | }
45 | if !templ_7745c5c3_IsBuffer {
46 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
47 | }
48 | return templ_7745c5c3_Err
49 | })
50 | }
51 |
52 | func Button(name string) templ.Component {
53 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
54 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
55 | if !templ_7745c5c3_IsBuffer {
56 | templ_7745c5c3_Buffer = templ.GetBuffer()
57 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
58 | }
59 | ctx = templ.InitializeContext(ctx)
60 | templ_7745c5c3_Var3 := templ.GetChildren(ctx)
61 | if templ_7745c5c3_Var3 == nil {
62 | templ_7745c5c3_Var3 = templ.NopComponent
63 | }
64 | ctx = templ.ClearChildren(ctx)
65 | templ_7745c5c3_Err = ButtonCSS().Render(ctx, templ_7745c5c3_Buffer)
66 | if templ_7745c5c3_Err != nil {
67 | return templ_7745c5c3_Err
68 | }
69 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
70 | if templ_7745c5c3_Err != nil {
71 | return templ_7745c5c3_Err
72 | }
73 | var templ_7745c5c3_Var4 string
74 | templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(name)
75 | if templ_7745c5c3_Err != nil {
76 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components.templ`, Line: 11, Col: 30}
77 | }
78 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
79 | if templ_7745c5c3_Err != nil {
80 | return templ_7745c5c3_Err
81 | }
82 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
83 | if templ_7745c5c3_Err != nil {
84 | return templ_7745c5c3_Err
85 | }
86 | if !templ_7745c5c3_IsBuffer {
87 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
88 | }
89 | return templ_7745c5c3_Err
90 | })
91 | }
92 |
--------------------------------------------------------------------------------
/examples/integration-templ/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/AccentDesign/gcss/examples/integration-templ
2 |
3 | go 1.22
4 |
5 | require (
6 | github.com/AccentDesign/gcss v0.1.0
7 | github.com/a-h/templ v0.2.707
8 | )
9 |
10 | replace github.com/AccentDesign/gcss => ../../
11 |
--------------------------------------------------------------------------------
/examples/integration-templ/go.sum:
--------------------------------------------------------------------------------
1 | github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
2 | github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
3 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5 |
--------------------------------------------------------------------------------
/examples/integration-templ/home.html:
--------------------------------------------------------------------------------
1 | Home Button One Button Two
--------------------------------------------------------------------------------
/examples/integration-templ/home.templ:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | templ Page() {
4 |
5 |
6 |
7 | Home
8 |
9 |
10 | @Button("Button One")
11 | @Button("Button Two")
12 |
13 |
14 | }
--------------------------------------------------------------------------------
/examples/integration-templ/home_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.707
4 | package main
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func Page() templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Home ")
27 | if templ_7745c5c3_Err != nil {
28 | return templ_7745c5c3_Err
29 | }
30 | templ_7745c5c3_Err = Button("Button One").Render(ctx, templ_7745c5c3_Buffer)
31 | if templ_7745c5c3_Err != nil {
32 | return templ_7745c5c3_Err
33 | }
34 | templ_7745c5c3_Err = Button("Button Two").Render(ctx, templ_7745c5c3_Buffer)
35 | if templ_7745c5c3_Err != nil {
36 | return templ_7745c5c3_Err
37 | }
38 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
39 | if templ_7745c5c3_Err != nil {
40 | return templ_7745c5c3_Err
41 | }
42 | if !templ_7745c5c3_IsBuffer {
43 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
44 | }
45 | return templ_7745c5c3_Err
46 | })
47 | }
48 |
--------------------------------------------------------------------------------
/examples/integration-templ/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "os"
6 | )
7 |
8 | func main() {
9 | file, err := os.Create("home.html")
10 | if err != nil {
11 | panic(err)
12 | }
13 | defer file.Close()
14 |
15 | home := Page()
16 | home.Render(context.Background(), file)
17 | }
18 |
--------------------------------------------------------------------------------
/examples/integration-templ/styles.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/variables"
6 | "github.com/a-h/templ"
7 | )
8 |
9 | var (
10 | buttonStyles = Stylesheet{
11 | {
12 | Selector: ".button",
13 | Props: gcss.Props{
14 | BackgroundColor: variables.Zinc800,
15 | BorderRadius: variables.Size1H,
16 | Color: variables.White,
17 | FontSize: variables.Size4,
18 | PaddingBottom: variables.Size3,
19 | PaddingLeft: variables.Size5,
20 | PaddingRight: variables.Size5,
21 | PaddingTop: variables.Size3,
22 | },
23 | },
24 | {
25 | Selector: ".button:hover",
26 | Props: gcss.Props{
27 | BackgroundColor: variables.Zinc900,
28 | },
29 | },
30 | }
31 | buttonStylesHandle = templ.NewOnceHandle()
32 | )
33 |
--------------------------------------------------------------------------------
/examples/integration-templ/stylesheet.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "github.com/AccentDesign/gcss"
6 | "io"
7 | )
8 |
9 | // Stylesheet is a collection of styles.
10 | type Stylesheet []gcss.Style
11 |
12 | // Render writes the CSS representation of the stylesheet to the writer.
13 | // this is to ensure that it implements a templ.Component.
14 | func (ss Stylesheet) Render(ctx context.Context, w io.Writer) error {
15 | //TODO: Get a CSP nonce from the context.
16 | if _, err := io.WriteString(w, ""); err != nil {
25 | return err
26 | }
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/examples/media-queries/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "io"
7 | "os"
8 | )
9 |
10 | type (
11 | Styles []gcss.Style
12 | Media struct {
13 | Query string
14 | Styles Styles
15 | }
16 | Stylesheet struct {
17 | Styles Styles
18 | Medias []Media
19 | }
20 | )
21 |
22 | // WriteCSS writes the CSS for the media query to the writer
23 | func (m Media) WriteCSS(w io.Writer) error {
24 | if _, err := io.WriteString(w, m.Query); err != nil {
25 | return err
26 | }
27 | if _, err := io.WriteString(w, "{"); err != nil {
28 | return err
29 | }
30 | for _, style := range m.Styles {
31 | if err := style.CSS(w); err != nil {
32 | return err
33 | }
34 | }
35 | if _, err := io.WriteString(w, "}"); err != nil {
36 | return err
37 | }
38 | return nil
39 | }
40 |
41 | // WriteCSS writes the CSS for the stylesheet to the writer
42 | func (ss Stylesheet) WriteCSS(w io.Writer) error {
43 | // Write the base styles first
44 | for _, style := range ss.Styles {
45 | if err := style.CSS(w); err != nil {
46 | return err
47 | }
48 | }
49 | // Write the media queries next
50 | for _, media := range ss.Medias {
51 | if err := media.WriteCSS(w); err != nil {
52 | return err
53 | }
54 | }
55 | return nil
56 | }
57 |
58 | var (
59 | base = Styles{
60 | {
61 | Selector: "body",
62 | Props: gcss.Props{
63 | Margin: props.UnitRaw(0),
64 | },
65 | },
66 | }
67 | screen736 = Media{
68 | Query: "@media only screen and (max-device-width: 736px)",
69 | Styles: Styles{
70 | {
71 | Selector: "main",
72 | Props: gcss.Props{
73 | Padding: props.UnitRaw(0),
74 | },
75 | },
76 | },
77 | }
78 | stylesheet = Stylesheet{
79 | Styles: base,
80 | Medias: []Media{screen736},
81 | }
82 | )
83 |
84 | // This is just a basic idea of how you could structure your CSS
85 | // the goal hear is just to wrap the css how you wish with what ever you wish
86 | // construct your stylesheet to suit your needs.
87 | // The end goal is just to call CSS on each style with the object to write to.
88 | func main() {
89 | file, err := os.Create("media.css")
90 | if err != nil {
91 | panic(err)
92 | }
93 | defer file.Close()
94 |
95 | if err := stylesheet.WriteCSS(file); err != nil {
96 | panic(err)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/examples/media-queries/media.css:
--------------------------------------------------------------------------------
1 | body{margin:0;}@media only screen and (max-device-width: 736px){main{padding:0;}}
--------------------------------------------------------------------------------
/examples/template-function/home.gohtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Click me
8 |
9 |
--------------------------------------------------------------------------------
/examples/template-function/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/AccentDesign/gcss"
6 | "github.com/AccentDesign/gcss/props"
7 | "github.com/AccentDesign/gcss/variables"
8 | "html/template"
9 | "net/http"
10 | "strings"
11 | )
12 |
13 | type Stylesheet []gcss.Style
14 |
15 | func (ss Stylesheet) CSS() (template.CSS, error) {
16 | var sb strings.Builder
17 | for _, style := range ss {
18 | if err := style.CSS(&sb); err != nil {
19 | return "", err
20 | }
21 | }
22 | return template.CSS(sb.String()), nil
23 | }
24 |
25 | var (
26 | styles = Stylesheet{
27 | {
28 | Selector: "html",
29 | Props: gcss.Props{
30 | FontFamily: props.FontFamilySans,
31 | },
32 | },
33 | {
34 | Selector: ".button",
35 | Props: gcss.Props{
36 | BackgroundColor: variables.Zinc800,
37 | Border: props.Border{
38 | Width: props.UnitPx(1),
39 | Style: props.BorderStyleSolid,
40 | Color: variables.Zinc900.Alpha(128),
41 | },
42 | BorderRadius: variables.Size1H,
43 | Color: variables.White,
44 | FontSize: variables.Size4,
45 | PaddingBottom: variables.Size3,
46 | PaddingLeft: variables.Size5,
47 | PaddingRight: variables.Size5,
48 | PaddingTop: variables.Size3,
49 | },
50 | },
51 | {
52 | Selector: ".button:hover",
53 | Props: gcss.Props{
54 | BackgroundColor: variables.Zinc900,
55 | },
56 | },
57 | }
58 | )
59 |
60 | var homeTemplate *template.Template
61 |
62 | func main() {
63 | var err error
64 | homeTemplate, err = template.New("home.gohtml").Funcs(template.FuncMap{
65 | "css": func() template.CSS {
66 | // todo: handle error
67 | css, _ := styles.CSS()
68 | return css
69 | },
70 | }).ParseFiles("home.gohtml")
71 | if err != nil {
72 | panic(err)
73 | }
74 |
75 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
76 | w.Header().Set("Content-Type", "text/html")
77 | err := homeTemplate.Execute(w, nil)
78 | if err != nil {
79 | http.Error(w, err.Error(), http.StatusInternalServerError)
80 | }
81 | })
82 |
83 | fmt.Println("Server started at http://localhost:8080")
84 |
85 | if err := http.ListenAndServe(":8080", nil); err != nil {
86 | panic(err)
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/examples/to-file/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | "os"
8 | )
9 |
10 | type Stylesheet []gcss.Style
11 |
12 | var styles = Stylesheet{
13 | {
14 | Selector: "html",
15 | Props: gcss.Props{
16 | FontFamily: props.FontFamilySans,
17 | },
18 | },
19 | {
20 | Selector: ".button",
21 | Props: gcss.Props{
22 | BackgroundColor: variables.Zinc800,
23 | Border: props.Border{
24 | Width: props.UnitPx(1),
25 | Style: props.BorderStyleSolid,
26 | Color: variables.Zinc900.Alpha(128),
27 | },
28 | BorderRadius: variables.Size1H,
29 | Color: variables.White,
30 | FontSize: variables.Size4,
31 | PaddingBottom: variables.Size3,
32 | PaddingLeft: variables.Size5,
33 | PaddingRight: variables.Size5,
34 | PaddingTop: variables.Size3,
35 | },
36 | },
37 | {
38 | Selector: ".button:hover",
39 | Props: gcss.Props{
40 | BackgroundColor: variables.Zinc900,
41 | },
42 | },
43 | }
44 |
45 | func main() {
46 | file, err := os.Create("stylesheet.css")
47 | if err != nil {
48 | panic(err)
49 | }
50 | defer file.Close()
51 |
52 | for _, style := range styles {
53 | if err := style.CSS(file); err != nil {
54 | panic(err)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/examples/to-http-handler/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/AccentDesign/gcss"
6 | "github.com/AccentDesign/gcss/props"
7 | "github.com/AccentDesign/gcss/variables"
8 | "net/http"
9 | )
10 |
11 | type Stylesheet []gcss.Style
12 |
13 | var (
14 | styles = Stylesheet{
15 | {
16 | Selector: "html",
17 | Props: gcss.Props{
18 | FontFamily: props.FontFamilySans,
19 | },
20 | },
21 | {
22 | Selector: ".button",
23 | Props: gcss.Props{
24 | BackgroundColor: variables.Zinc800,
25 | Border: props.Border{
26 | Width: props.UnitPx(1),
27 | Style: props.BorderStyleSolid,
28 | Color: variables.Zinc900.Alpha(128),
29 | },
30 | BorderRadius: variables.Size1H,
31 | Color: variables.White,
32 | FontSize: variables.Size4,
33 | PaddingBottom: variables.Size3,
34 | PaddingLeft: variables.Size5,
35 | PaddingRight: variables.Size5,
36 | PaddingTop: variables.Size3,
37 | },
38 | },
39 | {
40 | Selector: ".button:hover",
41 | Props: gcss.Props{
42 | BackgroundColor: variables.Zinc900,
43 | },
44 | },
45 | }
46 |
47 | html = `
48 |
49 |
50 |
51 |
52 |
53 |
54 | Click me
55 |
56 |
57 | `
58 | )
59 |
60 | func main() {
61 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
62 | if _, err := fmt.Fprint(w, html); err != nil {
63 | http.Error(w, err.Error(), http.StatusInternalServerError)
64 | }
65 | })
66 |
67 | http.HandleFunc("/stylesheet.css", func(w http.ResponseWriter, r *http.Request) {
68 | w.Header().Set("Content-Type", "text/css")
69 | for _, style := range styles {
70 | if err := style.CSS(w); err != nil {
71 | http.Error(w, err.Error(), http.StatusInternalServerError)
72 | }
73 | }
74 | })
75 |
76 | fmt.Println("Server started at http://localhost:8080")
77 |
78 | if err := http.ListenAndServe(":8080", nil); err != nil {
79 | panic(err)
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/examples/to-stdout/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/AccentDesign/gcss"
5 | "github.com/AccentDesign/gcss/props"
6 | "github.com/AccentDesign/gcss/variables"
7 | "os"
8 | )
9 |
10 | type Stylesheet []gcss.Style
11 |
12 | var styles = Stylesheet{
13 | {
14 | Selector: "html",
15 | Props: gcss.Props{
16 | FontFamily: props.FontFamilySans,
17 | },
18 | },
19 | {
20 | Selector: ".button",
21 | Props: gcss.Props{
22 | BackgroundColor: variables.Zinc800,
23 | Border: props.Border{
24 | Width: props.UnitPx(1),
25 | Style: props.BorderStyleSolid,
26 | Color: variables.Zinc900.Alpha(128),
27 | },
28 | BorderRadius: variables.Size1H,
29 | Color: variables.White,
30 | FontSize: variables.Size4,
31 | PaddingBottom: variables.Size3,
32 | PaddingLeft: variables.Size5,
33 | PaddingRight: variables.Size5,
34 | PaddingTop: variables.Size3,
35 | },
36 | },
37 | {
38 | Selector: ".button:hover",
39 | Props: gcss.Props{
40 | BackgroundColor: variables.Zinc900,
41 | },
42 | },
43 | }
44 |
45 | func main() {
46 | for _, style := range styles {
47 | if err := style.CSS(os.Stdout); err != nil {
48 | panic(err)
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/AccentDesign/gcss
2 |
3 | go 1.22
4 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AccentDesign/gcss/3b5aa22d0680d512ed6f3d2ae7807fddd80d9610/go.sum
--------------------------------------------------------------------------------
/props/align_content.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type AlignContent string
4 |
5 | const (
6 | AlignContentNormal AlignContent = "normal"
7 | AlignContentStart AlignContent = "start"
8 | AlignContentCenter AlignContent = "center"
9 | AlignContentEnd AlignContent = "end"
10 | AlignContentFlexStart AlignContent = "flex-start"
11 | AlignContentFlexEnd AlignContent = "flex-end"
12 | AlignContentBaseline AlignContent = "baseline"
13 | AlignContentFirstBaseline AlignContent = "first baseline"
14 | AlignContentLastBaseline AlignContent = "last baseline"
15 | AlignContentSpaceBetween AlignContent = "space-between"
16 | AlignContentSpaceAround AlignContent = "space-around"
17 | AlignContentSpaceEvenly AlignContent = "space-evenly"
18 | AlignContentStretch AlignContent = "stretch"
19 | )
20 |
21 | func (a AlignContent) String() string {
22 | return string(a)
23 | }
24 |
--------------------------------------------------------------------------------
/props/align_items.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type AlignItems string
4 |
5 | const (
6 | AlignItemsNormal AlignItems = "normal"
7 | AlignItemsStretch AlignItems = "stretch"
8 | AlignItemsStart AlignItems = "start"
9 | AlignItemsCenter AlignItems = "center"
10 | AlignItemsEnd AlignItems = "end"
11 | AlignItemsFlexStart AlignItems = "flex-start"
12 | AlignItemsFlexEnd AlignItems = "flex-end"
13 | AlignItemsSelfStart AlignItems = "self-start"
14 | AlignItemsSelfEnd AlignItems = "self-end"
15 | AlignItemsBaseline AlignItems = "baseline"
16 | )
17 |
18 | func (a AlignItems) String() string {
19 | return string(a)
20 | }
21 |
--------------------------------------------------------------------------------
/props/align_self.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type AlignSelf string
4 |
5 | const (
6 | AlignSelfAuto AlignSelf = "auto"
7 | AlignSelfNormal AlignSelf = "normal"
8 | AlignSelfStretch AlignSelf = "stretch"
9 | AlignSelfCenter AlignSelf = "center"
10 | AlignSelfStart AlignSelf = "start"
11 | AlignSelfEnd AlignSelf = "end"
12 | AlignSelfFlexStart AlignSelf = "flex-start"
13 | AlignSelfFlexEnd AlignSelf = "flex-end"
14 | AlignSelfSelfStart AlignSelf = "self-start"
15 | AlignSelfSelfEnd AlignSelf = "self-end"
16 | AlignSelfBaseline AlignSelf = "baseline"
17 | )
18 |
19 | func (a AlignSelf) String() string {
20 | return string(a)
21 | }
22 |
--------------------------------------------------------------------------------
/props/appearance.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Appearance string
4 |
5 | const (
6 | AppearanceNone Appearance = "none"
7 | AppearanceAuto Appearance = "auto"
8 | AppearanceMenuListButton Appearance = "menulist-button"
9 | AppearanceTextField Appearance = "textfield"
10 | AppearanceInherit Appearance = "inherit"
11 | AppearanceInitial Appearance = "initial"
12 | AppearanceRevert Appearance = "revert"
13 | AppearanceRevertLater Appearance = "revert-later"
14 | AppearanceUnset Appearance = "unset"
15 | AppearanceButton Appearance = "button"
16 | AppearanceCheckbox Appearance = "checkbox"
17 | )
18 |
19 | func (a Appearance) String() string {
20 | return string(a)
21 | }
22 |
--------------------------------------------------------------------------------
/props/background_image.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type BackgroundImage string
9 |
10 | func (b BackgroundImage) String() string {
11 | return string(b)
12 | }
13 |
14 | func BackgroundImages(images ...BackgroundImage) BackgroundImage {
15 | im := make([]string, len(images))
16 | for i, image := range images {
17 | im[i] = string(image)
18 | }
19 | return BackgroundImage(strings.Join(im, ","))
20 | }
21 |
22 | func BackgroundImageLinearGradient(segments ...string) BackgroundImage {
23 | return BackgroundImage(fmt.Sprintf("linear-gradient(%s)", strings.Join(segments, ",")))
24 | }
25 |
26 | func BackgroundImageURL(url string) BackgroundImage {
27 | return BackgroundImage(fmt.Sprintf(`url("%s")`, url))
28 | }
29 |
--------------------------------------------------------------------------------
/props/background_position.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type (
9 | BackgroundPosition string
10 | BackgroundPositionEdge struct {
11 | Position BackgroundPosition
12 | Unit Unit
13 | }
14 | )
15 |
16 | const (
17 | BackgroundPositionTop BackgroundPosition = "top"
18 | BackgroundPositionBottom BackgroundPosition = "bottom"
19 | BackgroundPositionLeft BackgroundPosition = "left"
20 | BackgroundPositionRight BackgroundPosition = "right"
21 | BackgroundPositionCenter BackgroundPosition = "center"
22 | BackgroundPositionTopLeft BackgroundPosition = "top left"
23 | BackgroundPositionTopRight BackgroundPosition = "top right"
24 | BackgroundPositionBottomLeft BackgroundPosition = "bottom left"
25 | BackgroundPositionBottomRight BackgroundPosition = "bottom right"
26 | )
27 |
28 | func (b BackgroundPosition) String() string {
29 | return string(b)
30 | }
31 |
32 | func (b BackgroundPositionEdge) String() string {
33 | return strings.TrimSpace(fmt.Sprintf("%s %s", b.Position, b.Unit))
34 | }
35 |
36 | func BackgroundPositionXY(x, y Unit) BackgroundPosition {
37 | return BackgroundPosition(fmt.Sprintf("%s %s", x.String(), y.String()))
38 | }
39 |
40 | func BackgroundPositionEdges(edges ...BackgroundPositionEdge) BackgroundPosition {
41 | ed := make([]string, len(edges))
42 | for i, edge := range edges {
43 | ed[i] = edge.String()
44 | }
45 | return BackgroundPosition(strings.Join(ed, " "))
46 | }
47 |
48 | func BackgroundPositions(positions ...BackgroundPosition) BackgroundPosition {
49 | po := make([]string, len(positions))
50 | for i, position := range positions {
51 | po[i] = string(position)
52 | }
53 | return BackgroundPosition(strings.Join(po, ","))
54 | }
55 |
--------------------------------------------------------------------------------
/props/background_repeat.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import "strings"
4 |
5 | type BackgroundRepeat string
6 |
7 | const (
8 | BackgroundRepeatRepeatX BackgroundRepeat = "repeat-x"
9 | BackgroundRepeatRepeatY BackgroundRepeat = "repeat-y"
10 | BackgroundRepeatRepeat BackgroundRepeat = "repeat"
11 | BackgroundRepeatSpace BackgroundRepeat = "space"
12 | BackgroundRepeatRound BackgroundRepeat = "round"
13 | BackgroundRepeatNoRepeat BackgroundRepeat = "no-repeat"
14 | )
15 |
16 | func (b BackgroundRepeat) String() string {
17 | return string(b)
18 | }
19 |
20 | func BackgroundRepeats(repeats ...BackgroundRepeat) BackgroundRepeat {
21 | var r []string
22 | for _, repeat := range repeats {
23 | r = append(r, string(repeat))
24 | }
25 | return BackgroundRepeat(strings.Join(r, ","))
26 | }
27 |
--------------------------------------------------------------------------------
/props/background_size.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import "strings"
4 |
5 | type BackgroundSize string
6 |
7 | func (b BackgroundSize) String() string {
8 | return string(b)
9 | }
10 |
11 | func BackgroundSizeWidth(unit Unit) BackgroundSize {
12 | return BackgroundSize(unit.String())
13 | }
14 |
15 | func BackgroundSizeDimension(width, height Unit) BackgroundSize {
16 | return BackgroundSize(width.String() + " " + height.String())
17 | }
18 |
19 | func BackgroundSizes(sizes ...BackgroundSize) BackgroundSize {
20 | var s []string
21 | for _, size := range sizes {
22 | s = append(s, string(size))
23 | }
24 | return BackgroundSize(strings.Join(s, ","))
25 | }
26 |
27 | func BackgroundSizeCover() BackgroundSize {
28 | return BackgroundSize("cover")
29 | }
30 |
31 | func BackgroundSizeContain() BackgroundSize {
32 | return BackgroundSize("contain")
33 | }
34 |
--------------------------------------------------------------------------------
/props/border.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | type (
8 | Border struct {
9 | Width Unit
10 | Style BorderStyle
11 | Color Color
12 | }
13 | )
14 |
15 | func (b Border) String() string {
16 | var parts []string
17 |
18 | if b.Width != (Unit{}) {
19 | parts = append(parts, b.Width.String())
20 | }
21 |
22 | if b.Style != "" {
23 | parts = append(parts, b.Style.String())
24 | }
25 |
26 | if b.Color != (Color{}) {
27 | parts = append(parts, b.Color.String())
28 | }
29 |
30 | return strings.TrimSpace(strings.Join(parts, " "))
31 | }
32 |
--------------------------------------------------------------------------------
/props/border_collapse.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type BorderCollapse string
4 |
5 | const (
6 | BorderCollapseCollapse BorderCollapse = "collapse"
7 | BorderCollapseSeparate BorderCollapse = "separate"
8 | )
9 |
10 | func (b BorderCollapse) String() string {
11 | return string(b)
12 | }
13 |
--------------------------------------------------------------------------------
/props/border_style.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type (
4 | BorderStyle string
5 | )
6 |
7 | const (
8 | BorderStyleSolid BorderStyle = "solid"
9 | BorderStyleDashed BorderStyle = "dashed"
10 | BorderStyleDotted BorderStyle = "dotted"
11 | BorderStyleDouble BorderStyle = "double"
12 | BorderStyleHidden BorderStyle = "hidden"
13 | BorderStyleNone BorderStyle = "none"
14 | )
15 |
16 | func (b BorderStyle) String() string {
17 | return string(b)
18 | }
19 |
--------------------------------------------------------------------------------
/props/box_sizing.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type BoxSizing string
4 |
5 | const (
6 | BoxSizingBorderBox BoxSizing = "border-box"
7 | BoxSizingContentBox BoxSizing = "content-box"
8 | )
9 |
10 | func (b BoxSizing) String() string {
11 | return string(b)
12 | }
13 |
--------------------------------------------------------------------------------
/props/caption_side.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type CaptionSide string
4 |
5 | const (
6 | CaptionSideTop CaptionSide = "top"
7 | CaptionSideBottom CaptionSide = "bottom"
8 | )
9 |
10 | func (c CaptionSide) String() string {
11 | return string(c)
12 | }
13 |
--------------------------------------------------------------------------------
/props/colors.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import (
4 | "fmt"
5 | "image/color"
6 | )
7 |
8 | type Color struct {
9 | Keyword string
10 | Color color.Color
11 | }
12 |
13 | // String returns a string representation of the color.
14 | // If the color is a keyword, the keyword is returned.
15 | // Otherwise, the color is returned as an rgba string.
16 | func (c Color) String() string {
17 | if c.Keyword != "" {
18 | return c.Keyword
19 | }
20 | r, g, b, a := c.Color.RGBA()
21 | return fmt.Sprintf("rgba(%d,%d,%d,%.2f)", r>>8, g>>8, b>>8, float32(a>>8)/255.0)
22 | }
23 |
24 | // Alpha returns a new color with the alpha channel set to the given value.
25 | func (c Color) Alpha(a uint8) Color {
26 | r, g, b, _ := c.Color.RGBA()
27 | c.Color = color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: a}
28 | return c
29 | }
30 |
31 | // Mix returns a new color that is a mixture of the two colors.
32 | // The percentage parameter is a float between 0 and 1
33 | // and determines how much of the second color to mix with the first color.
34 | func (c Color) Mix(with Color, percentage float64) Color {
35 | if percentage < 0 {
36 | percentage = 0
37 | }
38 | if percentage > 1 {
39 | percentage = 1
40 | }
41 | r1, g1, b1, a1 := c.Color.RGBA()
42 | r2, g2, b2, a2 := with.Color.RGBA()
43 | r := uint8(float64(r1>>8)*(1-percentage) + float64(r2>>8)*percentage)
44 | g := uint8(float64(g1>>8)*(1-percentage) + float64(g2>>8)*percentage)
45 | b := uint8(float64(b1>>8)*(1-percentage) + float64(b2>>8)*percentage)
46 | a := uint8(float64(a1>>8)*(1-percentage) + float64(a2>>8)*percentage)
47 | return Color{Color: color.RGBA{R: r, G: g, B: b, A: a}}
48 | }
49 |
50 | // ColorRGBA returns a new color with the given red, green, blue, and alpha values.
51 | func ColorRGBA(r, g, b, a uint8) Color {
52 | return Color{Color: color.RGBA{R: r, G: g, B: b, A: a}}
53 | }
54 |
55 | func ColorCurrentColor() Color {
56 | return Color{Keyword: "currentColor"}
57 | }
58 |
59 | func ColorInherit() Color {
60 | return Color{Keyword: "inherit"}
61 | }
62 |
63 | func ColorTransparent() Color {
64 | return Color{Keyword: "transparent"}
65 | }
66 |
--------------------------------------------------------------------------------
/props/colors_test.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import (
4 | "image/color"
5 | "testing"
6 | )
7 |
8 | func TestColor_String(t *testing.T) {
9 | testCases := map[Color]string{
10 | {Keyword: "currentColor"}: "currentColor",
11 | {Keyword: "inherit"}: "inherit",
12 | {Color: color.Gray{0}}: "rgba(0,0,0,1.00)",
13 | {Color: color.Gray{255}}: "rgba(255,255,255,1.00)",
14 | {Color: color.Gray16{0}}: "rgba(0,0,0,1.00)",
15 | {Color: color.Gray16{0xffff}}: "rgba(255,255,255,1.00)",
16 | ColorRGBA(62, 131, 248, 255): "rgba(62,131,248,1.00)",
17 | ColorRGBA(0, 0, 0, 0): "rgba(0,0,0,0.00)",
18 | ColorRGBA(255, 255, 255, 255): "rgba(255,255,255,1.00)",
19 | }
20 | for c, expected := range testCases {
21 | if c.String() != expected {
22 | t.Errorf("expected %v, got %v", expected, c.String())
23 | }
24 | }
25 | }
26 |
27 | func TestColor_Alpha(t *testing.T) {
28 | c := ColorRGBA(62, 131, 248, 255)
29 | expected := ColorRGBA(62, 131, 248, 128)
30 | if c.Alpha(128) != expected {
31 | t.Errorf("expected %v, got %v", expected, c.Alpha(128))
32 | }
33 | }
34 |
35 | func TestColor_MixToWhite(t *testing.T) {
36 | base := ColorRGBA(62, 131, 248, 255)
37 | with := ColorRGBA(255, 255, 255, 255)
38 | testCases := map[float64]Color{
39 | -1: ColorRGBA(62, 131, 248, 255),
40 | 0: ColorRGBA(62, 131, 248, 255),
41 | 0.1: ColorRGBA(81, 143, 248, 255),
42 | 0.2: ColorRGBA(100, 155, 249, 255),
43 | 0.3: ColorRGBA(119, 168, 250, 255),
44 | 0.4: ColorRGBA(139, 180, 250, 255),
45 | 0.5: ColorRGBA(158, 193, 251, 255),
46 | 0.6: ColorRGBA(177, 205, 252, 255),
47 | 0.7: ColorRGBA(197, 217, 252, 255),
48 | 0.8: ColorRGBA(216, 230, 253, 255),
49 | 0.9: ColorRGBA(235, 242, 254, 255),
50 | 1: ColorRGBA(255, 255, 255, 255),
51 | 1.1: ColorRGBA(255, 255, 255, 255),
52 | }
53 | for percentage, expected := range testCases {
54 | mixed := base.Mix(with, percentage)
55 | if mixed != expected {
56 | t.Errorf("%v expected %v, got %v", percentage, expected, mixed)
57 | }
58 | }
59 | }
60 |
61 | func TestColor_MixToBlack(t *testing.T) {
62 | base := ColorRGBA(62, 131, 248, 255)
63 | with := ColorRGBA(0, 0, 0, 255)
64 | testCases := map[float64]Color{
65 | -1: ColorRGBA(62, 131, 248, 255),
66 | 0: ColorRGBA(62, 131, 248, 255),
67 | 0.1: ColorRGBA(55, 117, 223, 255),
68 | 0.2: ColorRGBA(49, 104, 198, 255),
69 | 0.3: ColorRGBA(43, 91, 173, 255),
70 | 0.4: ColorRGBA(37, 78, 148, 255),
71 | 0.5: ColorRGBA(31, 65, 124, 255),
72 | 0.6: ColorRGBA(24, 52, 99, 255),
73 | 0.7: ColorRGBA(18, 39, 74, 255),
74 | 0.8: ColorRGBA(12, 26, 49, 255),
75 | 0.9: ColorRGBA(6, 13, 24, 255),
76 | 1: ColorRGBA(0, 0, 0, 255),
77 | 1.1: ColorRGBA(0, 0, 0, 255),
78 | }
79 | for percentage, expected := range testCases {
80 | mixed := base.Mix(with, percentage)
81 | if mixed != expected {
82 | t.Errorf("%v expected %v, got %v", percentage, expected, mixed)
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/props/cursor.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Cursor string
4 |
5 | const (
6 | CursorAuto Cursor = "auto"
7 | CursorDefault Cursor = "default"
8 | CursorPointer Cursor = "pointer"
9 | CursorWait Cursor = "wait"
10 | CursorText Cursor = "text"
11 | CursorMove Cursor = "move"
12 | CursorHelp Cursor = "help"
13 | CursorNotAllowed Cursor = "not-allowed"
14 | CursorNone Cursor = "none"
15 | )
16 |
17 | func (c Cursor) String() string {
18 | return string(c)
19 | }
20 |
--------------------------------------------------------------------------------
/props/display.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Display string
4 |
5 | const (
6 | DisplayBlock Display = "block"
7 | DisplayInlineBlock Display = "inline-block"
8 | DisplayInline Display = "inline"
9 | DisplayFlex Display = "flex"
10 | DisplayInlineFlex Display = "inline-flex"
11 | DisplayTable Display = "table"
12 | DisplayInlineTable Display = "inline-table"
13 | DisplayTableCaption Display = "table-caption"
14 | DisplayTableCell Display = "table-cell"
15 | DisplayTableColumn Display = "table-column"
16 | DisplayTableColumnGroup Display = "table-column-group"
17 | DisplayTableFooterGroup Display = "table-footer-group"
18 | DisplayTableHeaderGroup Display = "table-header-group"
19 | DisplayTableRowGroup Display = "table-row-group"
20 | DisplayTableRow Display = "table-row"
21 | DisplayFlowRoot Display = "flow-root"
22 | DisplayGrid Display = "grid"
23 | DisplayInlineGrid Display = "inline-grid"
24 | DisplayContents Display = "contents"
25 | DisplayListItem Display = "list-item"
26 | DisplayNone Display = "none"
27 | )
28 |
29 | func (d Display) String() string {
30 | return string(d)
31 | }
32 |
--------------------------------------------------------------------------------
/props/flex_direction.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type FlexDirection string
4 |
5 | const (
6 | FlexDirectionColumn FlexDirection = "column"
7 | FlexDirectionColumnReverse FlexDirection = "column-reverse"
8 | FlexDirectionRow FlexDirection = "row"
9 | FlexDirectionRowReverse FlexDirection = "row-reverse"
10 | )
11 |
12 | func (f FlexDirection) String() string {
13 | return string(f)
14 | }
15 |
--------------------------------------------------------------------------------
/props/flex_wrap.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type FlexWrap string
4 |
5 | const (
6 | FlexWrapNoWrap FlexWrap = "nowrap"
7 | FlexWrapWrap FlexWrap = "wrap"
8 | FlexWrapWrapReverse FlexWrap = "wrap-reverse"
9 | )
10 |
11 | func (f FlexWrap) String() string {
12 | return string(f)
13 | }
14 |
--------------------------------------------------------------------------------
/props/float.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Float string
4 |
5 | const (
6 | FloatNone Float = "none"
7 | FloatLeft Float = "left"
8 | FloatRight Float = "right"
9 | FloatInlineStart Float = "inline-start"
10 | FloatInlineEnd Float = "inline-end"
11 | )
12 |
13 | func (f Float) String() string {
14 | return string(f)
15 | }
16 |
--------------------------------------------------------------------------------
/props/font_family.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type FontFamily string
4 |
5 | const (
6 | FontFamilySans FontFamily = "ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\""
7 | FontFamilySerif FontFamily = "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif"
8 | FontFamilyMono FontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
9 | )
10 |
11 | func (f FontFamily) String() string {
12 | return string(f)
13 | }
14 |
--------------------------------------------------------------------------------
/props/font_style.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type FontStyle string
4 |
5 | const (
6 | FontStyleNormal FontStyle = "normal"
7 | FontStyleItalic FontStyle = "italic"
8 | )
9 |
10 | func (f FontStyle) String() string {
11 | return string(f)
12 | }
13 |
--------------------------------------------------------------------------------
/props/font_weight.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type FontWeight string
4 |
5 | const (
6 | FontWeightThin FontWeight = "100"
7 | FontWeightExtraLight FontWeight = "200"
8 | FontWeightLight FontWeight = "300"
9 | FontWeightNormal FontWeight = "400"
10 | FontWeightMedium FontWeight = "500"
11 | FontWeightSemiBold FontWeight = "600"
12 | FontWeightBold FontWeight = "700"
13 | FontWeightExtraBold FontWeight = "800"
14 | FontWeightBlack FontWeight = "900"
15 | )
16 |
17 | func (f FontWeight) String() string {
18 | return string(f)
19 | }
20 |
--------------------------------------------------------------------------------
/props/justify_content.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type JustifyContent string
4 |
5 | const (
6 | JustifyContentNormal JustifyContent = "normal"
7 | JustifyContentCenter JustifyContent = "center"
8 | JustifyContentStart JustifyContent = "start"
9 | JustifyContentEnd JustifyContent = "end"
10 | JustifyContentFlexStart JustifyContent = "flex-start"
11 | JustifyContentFlexEnd JustifyContent = "flex-end"
12 | JustifyContentLeft JustifyContent = "left"
13 | JustifyContentRight JustifyContent = "right"
14 | JustifyContentSpaceBetween JustifyContent = "space-between"
15 | JustifyContentSpaceAround JustifyContent = "space-around"
16 | JustifyContentSpaceEvenly JustifyContent = "space-evenly"
17 | JustifyContentStretch JustifyContent = "stretch"
18 | )
19 |
20 | func (j JustifyContent) String() string {
21 | return string(j)
22 | }
23 |
--------------------------------------------------------------------------------
/props/justify_items.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type JustifyItems string
4 |
5 | const (
6 | JustifyItemsNormal JustifyItems = "normal"
7 | JustifyItemsStretch JustifyItems = "stretch"
8 | JustifyItemsCenter JustifyItems = "center"
9 | JustifyItemsStart JustifyItems = "start"
10 | JustifyItemsEnd JustifyItems = "end"
11 | JustifyItemsFlexStart JustifyItems = "flex-start"
12 | JustifyItemsFlexEnd JustifyItems = "flex-end"
13 | JustifyItemsSelfStart JustifyItems = "self-start"
14 | JustifyItemsSelfEnd JustifyItems = "self-end"
15 | JustifyItemsLeft JustifyItems = "left"
16 | JustifyItemsRight JustifyItems = "right"
17 | JustifyItemsBaseline JustifyItems = "baseline"
18 | )
19 |
20 | func (j JustifyItems) String() string {
21 | return string(j)
22 | }
23 |
--------------------------------------------------------------------------------
/props/justify_self.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type JustifySelf string
4 |
5 | const (
6 | JustifySelfAuto JustifySelf = "auto"
7 | JustifySelfNormal JustifySelf = "normal"
8 | JustifySelfStretch JustifySelf = "stretch"
9 | JustifySelfCenter JustifySelf = "center"
10 | JustifySelfStart JustifySelf = "start"
11 | JustifySelfEnd JustifySelf = "end"
12 | JustifySelfFlexStart JustifySelf = "flex-start"
13 | JustifySelfFlexEnd JustifySelf = "flex-end"
14 | JustifySelfSelfStart JustifySelf = "self-start"
15 | JustifySelfSelfEnd JustifySelf = "self-end"
16 | JustifySelfLeft JustifySelf = "left"
17 | JustifySelfRight JustifySelf = "right"
18 | JustifySelfBaseline JustifySelf = "baseline"
19 | )
20 |
21 | func (j JustifySelf) String() string {
22 | return string(j)
23 | }
24 |
--------------------------------------------------------------------------------
/props/list_style_position.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type ListStylePosition string
4 |
5 | const (
6 | ListStylePositionInside ListStylePosition = "inside"
7 | ListStylePositionOutside ListStylePosition = "outside"
8 | )
9 |
10 | func (l ListStylePosition) String() string {
11 | return string(l)
12 | }
13 |
--------------------------------------------------------------------------------
/props/list_style_type.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type ListStyleType string
4 |
5 | const (
6 | ListStyleTypeNone ListStyleType = "none"
7 | ListStyleTypeDisc ListStyleType = "disc"
8 | ListStyleTypeDecimal ListStyleType = "decimal"
9 | )
10 |
11 | func (l ListStyleType) String() string {
12 | return string(l)
13 | }
14 |
--------------------------------------------------------------------------------
/props/overflow.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Overflow string
4 |
5 | const (
6 | OverflowVisible Overflow = "visible"
7 | OverflowHidden Overflow = "hidden"
8 | OverflowScroll Overflow = "scroll"
9 | OverflowAuto Overflow = "auto"
10 | OverflowClip Overflow = "clip"
11 | )
12 |
13 | func (o Overflow) String() string {
14 | return string(o)
15 | }
16 |
--------------------------------------------------------------------------------
/props/position.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Position string
4 |
5 | const (
6 | PositionStatic Position = "static"
7 | PositionRelative Position = "relative"
8 | PositionAbsolute Position = "absolute"
9 | PositionFixed Position = "fixed"
10 | PositionSticky Position = "sticky"
11 | )
12 |
13 | func (p Position) String() string {
14 | return string(p)
15 | }
16 |
--------------------------------------------------------------------------------
/props/print_color_adjust.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type PrintColorAdjust string
4 |
5 | const (
6 | PrintColorAdjustEconomy PrintColorAdjust = "economy"
7 | PrintColorAdjustExact PrintColorAdjust = "exact"
8 | )
9 |
10 | func (c PrintColorAdjust) String() string {
11 | return string(c)
12 | }
13 |
--------------------------------------------------------------------------------
/props/text_align.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextAlign string
4 |
5 | const (
6 | TextAlignLeft TextAlign = "left"
7 | TextAlignRight TextAlign = "right"
8 | TextAlignCenter TextAlign = "center"
9 | TextAlignJustify TextAlign = "justify"
10 | TextAlignStart TextAlign = "start"
11 | TextAlignEnd TextAlign = "end"
12 | )
13 |
14 | func (t TextAlign) String() string {
15 | return string(t)
16 | }
17 |
--------------------------------------------------------------------------------
/props/text_decoration_line.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextDecorationLine string
4 |
5 | const (
6 | TextDecorationLineNone TextDecorationLine = "none"
7 | TextDecorationLineUnderline TextDecorationLine = "underline"
8 | TextDecorationLineOverline TextDecorationLine = "overline"
9 | TextDecorationLineLineThrough TextDecorationLine = "line-through"
10 | )
11 |
12 | func (t TextDecorationLine) String() string {
13 | return string(t)
14 | }
15 |
--------------------------------------------------------------------------------
/props/text_decoration_style.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextDecorationStyle string
4 |
5 | const (
6 | TextDecorationStyleSolid TextDecorationStyle = "solid"
7 | TextDecorationStyleDouble TextDecorationStyle = "double"
8 | TextDecorationStyleDotted TextDecorationStyle = "dotted"
9 | TextDecorationStyleDashed TextDecorationStyle = "dashed"
10 | TextDecorationStyleWavy TextDecorationStyle = "wavy"
11 | )
12 |
13 | func (t TextDecorationStyle) String() string {
14 | return string(t)
15 | }
16 |
--------------------------------------------------------------------------------
/props/text_overflow.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextOverflow string
4 |
5 | const (
6 | TextOverflowClip TextOverflow = "clip"
7 | TextOverflowEllipsis TextOverflow = "ellipsis"
8 | )
9 |
10 | func (t TextOverflow) String() string {
11 | return string(t)
12 | }
13 |
--------------------------------------------------------------------------------
/props/text_transform.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextTransform string
4 |
5 | const (
6 | TextTransformNone TextTransform = "none"
7 | TextTransformCapitalize TextTransform = "capitalize"
8 | TextTransformUppercase TextTransform = "uppercase"
9 | TextTransformLowercase TextTransform = "lowercase"
10 | )
11 |
12 | func (t TextTransform) String() string {
13 | return string(t)
14 | }
15 |
--------------------------------------------------------------------------------
/props/text_wrap.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type TextWrap string
4 |
5 | const (
6 | TextWrapWrap TextWrap = "wrap"
7 | TextWrapNoWrap TextWrap = "nowrap"
8 | TextWrapBalance TextWrap = "balance"
9 | )
10 |
11 | func (t TextWrap) String() string {
12 | return string(t)
13 | }
14 |
--------------------------------------------------------------------------------
/props/unit.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | import "fmt"
4 |
5 | type (
6 | UnitType int
7 | Unit struct {
8 | Size interface{}
9 | Type UnitType
10 | }
11 | )
12 |
13 | const (
14 | _ UnitType = iota
15 | UnitTypeRaw
16 | UnitTypePx
17 | UnitTypePercent
18 | UnitTypeRem
19 | UnitTypeEm
20 | UnitTypeVh
21 | UnitTypeVw
22 | UnitTypeAuto
23 | UnitTypeInherit
24 | UnitTypeInitial
25 | )
26 |
27 | func (u Unit) String() string {
28 | switch u.Type {
29 | case UnitTypeRaw:
30 | return fmt.Sprintf("%v", u.Size)
31 | case UnitTypePx, UnitTypePercent, UnitTypeRem, UnitTypeEm, UnitTypeVh, UnitTypeVw:
32 | return formatSize(u)
33 | case UnitTypeAuto:
34 | return "auto"
35 | case UnitTypeInherit:
36 | return "inherit"
37 | case UnitTypeInitial:
38 | return "initial"
39 | }
40 | return ""
41 | }
42 |
43 | func formatSize(u Unit) string {
44 | format := getFormat(u.Type)
45 | switch v := u.Size.(type) {
46 | case int:
47 | return fmt.Sprintf(format, v)
48 | case float64:
49 | return fmt.Sprintf(format, v)
50 | default:
51 | return ""
52 | }
53 | }
54 |
55 | func getFormat(unitType UnitType) string {
56 | switch unitType {
57 | case UnitTypePx:
58 | return "%dpx"
59 | case UnitTypePercent:
60 | return "%.2f%%"
61 | case UnitTypeRem:
62 | return "%.3frem"
63 | case UnitTypeEm:
64 | return "%.3fem"
65 | case UnitTypeVh:
66 | return "%dvh"
67 | case UnitTypeVw:
68 | return "%dvw"
69 | default:
70 | return ""
71 | }
72 | }
73 |
74 | func UnitRaw(size interface{}) Unit {
75 | return Unit{Size: size, Type: UnitTypeRaw}
76 | }
77 |
78 | func UnitPx(size int) Unit {
79 | return Unit{Size: size, Type: UnitTypePx}
80 | }
81 |
82 | func UnitPercent(size float64) Unit {
83 | return Unit{Size: size, Type: UnitTypePercent}
84 | }
85 |
86 | func UnitRem(size float64) Unit {
87 | return Unit{Size: size, Type: UnitTypeRem}
88 | }
89 |
90 | func UnitEm(size float64) Unit {
91 | return Unit{Size: size, Type: UnitTypeEm}
92 | }
93 |
94 | func UnitVh(size int) Unit {
95 | return Unit{Size: size, Type: UnitTypeVh}
96 | }
97 |
98 | func UnitVw(size int) Unit {
99 | return Unit{Size: size, Type: UnitTypeVw}
100 | }
101 |
102 | func UnitAuto() Unit {
103 | return Unit{Type: UnitTypeAuto}
104 | }
105 |
106 | func UnitInherit() Unit {
107 | return Unit{Type: UnitTypeInherit}
108 | }
109 |
110 | func UnitInitial() Unit {
111 | return Unit{Type: UnitTypeInitial}
112 | }
113 |
--------------------------------------------------------------------------------
/props/verticle_align.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type VerticalAlign string
4 |
5 | const (
6 | VerticalAlignBaseline VerticalAlign = "baseline"
7 | VerticalAlignSub VerticalAlign = "sub"
8 | VerticalAlignSuper VerticalAlign = "super"
9 | VerticalAlignTextTop VerticalAlign = "text-top"
10 | VerticalAlignTextBottom VerticalAlign = "text-bottom"
11 | VerticalAlignMiddle VerticalAlign = "middle"
12 | VerticalAlignTop VerticalAlign = "top"
13 | VerticalAlignBottom VerticalAlign = "bottom"
14 | )
15 |
16 | func (v VerticalAlign) String() string {
17 | return string(v)
18 | }
19 |
--------------------------------------------------------------------------------
/props/visibility.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type Visibility string
4 |
5 | const (
6 | VisibilityVisible Visibility = "visible"
7 | VisibilityHidden Visibility = "hidden"
8 | VisibilityCollapse Visibility = "collapse"
9 | )
10 |
11 | func (v Visibility) String() string {
12 | return string(v)
13 | }
14 |
--------------------------------------------------------------------------------
/props/white_space.go:
--------------------------------------------------------------------------------
1 | package props
2 |
3 | type WhiteSpace string
4 |
5 | const (
6 | WhiteSpaceNormal WhiteSpace = "normal"
7 | WhiteSpaceNowrap WhiteSpace = "nowrap"
8 | WhiteSpacePre WhiteSpace = "pre"
9 | WhiteSpacePreLine WhiteSpace = "pre-line"
10 | WhiteSpacePreWrap WhiteSpace = "pre-wrap"
11 | )
12 |
13 | func (w WhiteSpace) String() string {
14 | return string(w)
15 | }
16 |
--------------------------------------------------------------------------------
/style.go:
--------------------------------------------------------------------------------
1 | package gcss
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/AccentDesign/gcss/props"
7 | "io"
8 | "reflect"
9 | )
10 |
11 | type (
12 | // Props represents the standard CSS properties that can be applied to a CSS rule.
13 | Props struct {
14 | AlignContent props.AlignContent `css:"align-content"`
15 | AlignItems props.AlignItems `css:"align-items"`
16 | AlignSelf props.AlignSelf `css:"align-self"`
17 | Appearance props.Appearance `css:"appearance"`
18 | BackgroundColor props.Color `css:"background-color"`
19 | BackgroundImage props.BackgroundImage `css:"background-image"`
20 | BackgroundPosition props.BackgroundPosition `css:"background-position"`
21 | BackgroundRepeat props.BackgroundRepeat `css:"background-repeat"`
22 | BackgroundSize props.BackgroundSize `css:"background-size"`
23 | Border props.Border `css:"border"`
24 | BorderBottom props.Border `css:"border-bottom"`
25 | BorderBottomLeftRadius props.Unit `css:"border-bottom-left-radius"`
26 | BorderBottomRightRadius props.Unit `css:"border-bottom-right-radius"`
27 | BorderCollapse props.BorderCollapse `css:"border-collapse"`
28 | BorderColor props.Color `css:"border-color"`
29 | BorderLeft props.Border `css:"border-left"`
30 | BorderRadius props.Unit `css:"border-radius"`
31 | BorderRight props.Border `css:"border-right"`
32 | BorderStyle props.BorderStyle `css:"border-style"`
33 | BorderTop props.Border `css:"border-top"`
34 | BorderTopLeftRadius props.Unit `css:"border-top-left-radius"`
35 | BorderTopRightRadius props.Unit `css:"border-top-right-radius"`
36 | BorderWidth props.Unit `css:"border-width"`
37 | Bottom props.Unit `css:"bottom"`
38 | BoxSizing props.BoxSizing `css:"box-sizing"`
39 | CaptionSide props.CaptionSide `css:"caption-side"`
40 | Color props.Color `css:"color"`
41 | ColumnGap props.Unit `css:"column-gap"`
42 | Cursor props.Cursor `css:"cursor"`
43 | Display props.Display `css:"display"`
44 | FlexBasis props.Unit `css:"flex-basis"`
45 | FlexDirection props.FlexDirection `css:"flex-direction"`
46 | FlexGrow props.Unit `css:"flex-grow"`
47 | FlexShrink props.Unit `css:"flex-shrink"`
48 | FlexWrap props.FlexWrap `css:"flex-wrap"`
49 | Float props.Float `css:"float"`
50 | FontFamily props.FontFamily `css:"font-family"`
51 | FontSize props.Unit `css:"font-size"`
52 | FontStyle props.FontStyle `css:"font-style"`
53 | FontWeight props.FontWeight `css:"font-weight"`
54 | Gap props.Unit `css:"gap"`
55 | Height props.Unit `css:"height"`
56 | JustifyContent props.JustifyContent `css:"justify-content"`
57 | JustifyItems props.JustifyItems `css:"justify-items"`
58 | JustifySelf props.JustifySelf `css:"justify-self"`
59 | Left props.Unit `css:"left"`
60 | LineHeight props.Unit `css:"line-height"`
61 | ListStylePosition props.ListStylePosition `css:"list-style-position"`
62 | ListStyleType props.ListStyleType `css:"list-style-type"`
63 | Margin props.Unit `css:"margin"`
64 | MarginBottom props.Unit `css:"margin-bottom"`
65 | MarginLeft props.Unit `css:"margin-left"`
66 | MarginRight props.Unit `css:"margin-right"`
67 | MarginTop props.Unit `css:"margin-top"`
68 | MaxHeight props.Unit `css:"max-height"`
69 | MaxWidth props.Unit `css:"max-width"`
70 | MinHeight props.Unit `css:"min-height"`
71 | MinWidth props.Unit `css:"min-width"`
72 | Opacity props.Unit `css:"opacity"`
73 | Overflow props.Overflow `css:"overflow"`
74 | OverflowX props.Overflow `css:"overflow-x"`
75 | OverflowY props.Overflow `css:"overflow-y"`
76 | Padding props.Unit `css:"padding"`
77 | PaddingBottom props.Unit `css:"padding-bottom"`
78 | PaddingLeft props.Unit `css:"padding-left"`
79 | PaddingRight props.Unit `css:"padding-right"`
80 | PaddingTop props.Unit `css:"padding-top"`
81 | Position props.Position `css:"position"`
82 | PrintColorAdjust props.PrintColorAdjust `css:"print-color-adjust"`
83 | Right props.Unit `css:"right"`
84 | RowGap props.Unit `css:"row-gap"`
85 | TextAlign props.TextAlign `css:"text-align"`
86 | TextDecorationColor props.Color `css:"text-decoration-color"`
87 | TextDecorationLine props.TextDecorationLine `css:"text-decoration-line"`
88 | TextDecorationStyle props.TextDecorationStyle `css:"text-decoration-style"`
89 | TextDecorationThickness props.Unit `css:"text-decoration-thickness"`
90 | TextIndent props.Unit `css:"text-indent"`
91 | TextOverflow props.TextOverflow `css:"text-overflow"`
92 | TextTransform props.TextTransform `css:"text-transform"`
93 | TextUnderlineOffset props.Unit `css:"text-underline-offset"`
94 | TextWrap props.TextWrap `css:"text-wrap"`
95 | Top props.Unit `css:"top"`
96 | VerticalAlign props.VerticalAlign `css:"vertical-align"`
97 | WhiteSpace props.WhiteSpace `css:"white-space"`
98 | Width props.Unit `css:"width"`
99 | Visibility props.Visibility `css:"visibility"`
100 | ZIndex props.Unit `css:"z-index"`
101 | }
102 | // CustomProp represents an additional CSS property that is not covered by the Props struct.
103 | CustomProp struct {
104 | Attr, Value string
105 | }
106 | // Style represents a CSS style rule.
107 | Style struct {
108 | // Selector is the CSS selector to which the properties will be applied.
109 | // It can be any valid CSS selector like class, id, element type, etc.
110 | Selector string
111 |
112 | // Props contains the standard CSS properties that will be applied to the selector.
113 | // These properties are represented by the Props struct and are strongly typed.
114 | Props Props
115 |
116 | // CustomProps contains any additional CSS properties that are not covered by the Props struct.
117 | // These properties are directly added to the CSS rule as is.
118 | CustomProps []CustomProp
119 | }
120 | )
121 |
122 | // CSS writes the CSS representation of the props to the writer.
123 | func (p *Props) CSS(w io.Writer) error {
124 | value := reflect.ValueOf(*p)
125 | typ := reflect.TypeOf(*p)
126 |
127 | // Iterate over the fields of the Props struct and write the CSS properties to the writer.
128 | for i := 0; i < value.NumField(); i++ {
129 | fieldValue := value.Field(i)
130 | fieldType := typ.Field(i)
131 |
132 | if fieldValue.IsZero() {
133 | continue
134 | }
135 |
136 | fieldName := fieldType.Tag.Get("css")
137 |
138 | if v, ok := fieldValue.Interface().(fmt.Stringer); ok {
139 | if _, err := fmt.Fprintf(w, "%s:%s;", fieldName, v.String()); err != nil {
140 | return err
141 | }
142 | }
143 | }
144 |
145 | return nil
146 | }
147 |
148 | // CSS writes the CSS representation of the style to the writer.
149 | func (s *Style) CSS(w io.Writer) error {
150 | var buf bytes.Buffer
151 |
152 | // Write the standard properties to the writer.
153 | if err := s.Props.CSS(&buf); err != nil {
154 | return err
155 | }
156 |
157 | // Write the custom properties to the writer.
158 | for _, prop := range s.CustomProps {
159 | if _, err := fmt.Fprintf(&buf, "%s:%s;", prop.Attr, prop.Value); err != nil {
160 | return err
161 | }
162 | }
163 |
164 | if buf.Len() > 0 {
165 | _, err := fmt.Fprintf(w, "%s{%s}", s.Selector, buf.String())
166 | return err
167 | }
168 |
169 | return nil
170 | }
171 |
--------------------------------------------------------------------------------
/style_test.go:
--------------------------------------------------------------------------------
1 | package gcss
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/AccentDesign/gcss/props"
7 | "image/color"
8 | "testing"
9 | )
10 |
11 | func runTest(t *testing.T, st *Style, expected string) {
12 | var buf bytes.Buffer
13 | err := st.CSS(&buf)
14 | if err != nil {
15 | t.Errorf("unexpected error: %v", err)
16 | }
17 |
18 | if buf.String() != expected {
19 | t.Errorf("expected %q, got %q", expected, buf.String())
20 | }
21 | }
22 |
23 | func TestProps_CSS(t *testing.T) {
24 | testCases := map[Props]string{
25 | {}: "",
26 | {BackgroundColor: props.ColorRGBA(0, 0, 0, 255)}: "background-color:rgba(0,0,0,1.00);",
27 | {Margin: props.UnitPx(10), Padding: props.UnitPx(10)}: "margin:10px;padding:10px;",
28 | }
29 | for prop, expected := range testCases {
30 | var buf bytes.Buffer
31 | err := prop.CSS(&buf)
32 | if err != nil {
33 | t.Errorf("unexpected error: %v", err)
34 | }
35 |
36 | if buf.String() != expected {
37 | t.Errorf("expected %q, got %q", expected, buf.String())
38 | }
39 | }
40 | }
41 |
42 | func TestStyle_Empty(t *testing.T) {
43 | st := &Style{Selector: ".test", Props: Props{}}
44 | css := ""
45 | runTest(t, st, css)
46 | }
47 |
48 | func TestStyle_MultipleProps(t *testing.T) {
49 | st := &Style{Selector: ".test", Props: Props{
50 | BackgroundColor: props.ColorRGBA(0, 0, 0, 255),
51 | Height: props.UnitPx(100),
52 | Width: props.UnitPx(100),
53 | }}
54 | css := ".test{background-color:rgba(0,0,0,1.00);height:100px;width:100px;}"
55 | runTest(t, st, css)
56 | }
57 |
58 | func TestStyle_CustomProps(t *testing.T) {
59 | st := &Style{
60 | Selector: ".test",
61 | CustomProps: []CustomProp{
62 | {Attr: "--color", Value: "red"},
63 | {Attr: "background-color", Value: "var(--color)"},
64 | },
65 | }
66 | css := ".test{--color:red;background-color:var(--color);}"
67 | runTest(t, st, css)
68 | }
69 |
70 | func TestStyle_AlignContent(t *testing.T) {
71 | testCases := map[props.AlignContent]string{
72 | props.AlignContentNormal: "normal",
73 | props.AlignContentStart: "start",
74 | props.AlignContentCenter: "center",
75 | props.AlignContentEnd: "end",
76 | props.AlignContentFlexStart: "flex-start",
77 | props.AlignContentFlexEnd: "flex-end",
78 | props.AlignContentBaseline: "baseline",
79 | props.AlignContentFirstBaseline: "first baseline",
80 | props.AlignContentLastBaseline: "last baseline",
81 | props.AlignContentSpaceBetween: "space-between",
82 | props.AlignContentSpaceAround: "space-around",
83 | props.AlignContentSpaceEvenly: "space-evenly",
84 | props.AlignContentStretch: "stretch",
85 | props.AlignContent("inherit"): "inherit",
86 | }
87 |
88 | for prop, expected := range testCases {
89 | st := &Style{Selector: ".test", Props: Props{AlignContent: prop}}
90 | css := fmt.Sprintf(".test{align-content:%s;}", expected)
91 | runTest(t, st, css)
92 | }
93 | }
94 |
95 | func TestStyle_AlignItems(t *testing.T) {
96 | testCases := map[props.AlignItems]string{
97 | props.AlignItemsNormal: "normal",
98 | props.AlignItemsStart: "start",
99 | props.AlignItemsCenter: "center",
100 | props.AlignItemsEnd: "end",
101 | props.AlignItemsFlexStart: "flex-start",
102 | props.AlignItemsFlexEnd: "flex-end",
103 | props.AlignItemsSelfStart: "self-start",
104 | props.AlignItemsSelfEnd: "self-end",
105 | props.AlignItemsBaseline: "baseline",
106 | props.AlignItemsStretch: "stretch",
107 | props.AlignItems("inherit"): "inherit",
108 | }
109 |
110 | for prop, expected := range testCases {
111 | st := &Style{Selector: ".test", Props: Props{AlignItems: prop}}
112 | css := fmt.Sprintf(".test{align-items:%s;}", expected)
113 | runTest(t, st, css)
114 | }
115 | }
116 |
117 | func TestStyle_AlignSelf(t *testing.T) {
118 | testCases := map[props.AlignSelf]string{
119 | props.AlignSelfAuto: "auto",
120 | props.AlignSelfNormal: "normal",
121 | props.AlignSelfStretch: "stretch",
122 | props.AlignSelfCenter: "center",
123 | props.AlignSelfStart: "start",
124 | props.AlignSelfEnd: "end",
125 | props.AlignSelfFlexStart: "flex-start",
126 | props.AlignSelfFlexEnd: "flex-end",
127 | props.AlignSelfSelfStart: "self-start",
128 | props.AlignSelfSelfEnd: "self-end",
129 | props.AlignSelfBaseline: "baseline",
130 | props.AlignSelf("inherit"): "inherit",
131 | }
132 |
133 | for prop, expected := range testCases {
134 | st := &Style{Selector: ".test", Props: Props{AlignSelf: prop}}
135 | css := fmt.Sprintf(".test{align-self:%s;}", expected)
136 | runTest(t, st, css)
137 | }
138 | }
139 |
140 | func TestStyle_Appearance(t *testing.T) {
141 | testCases := map[props.Appearance]string{
142 | props.AppearanceNone: "none",
143 | props.AppearanceAuto: "auto",
144 | props.AppearanceMenuListButton: "menulist-button",
145 | props.AppearanceTextField: "textfield",
146 | props.AppearanceInherit: "inherit",
147 | props.AppearanceInitial: "initial",
148 | props.AppearanceRevert: "revert",
149 | props.AppearanceRevertLater: "revert-later",
150 | props.AppearanceUnset: "unset",
151 | props.AppearanceButton: "button",
152 | props.AppearanceCheckbox: "checkbox",
153 | }
154 |
155 | for prop, expected := range testCases {
156 | st := &Style{Selector: ".test", Props: Props{Appearance: prop}}
157 | css := fmt.Sprintf(".test{appearance:%s;}", expected)
158 | runTest(t, st, css)
159 | }
160 | }
161 |
162 | func TestStyle_BackgroundColor(t *testing.T) {
163 | testCases := map[props.Color]string{
164 | props.ColorRGBA(0, 0, 0, 255): "rgba(0,0,0,1.00)",
165 | props.ColorRGBA(255, 255, 255, 230): "rgba(255,255,255,0.90)",
166 | props.ColorRGBA(255, 255, 255, 255).Alpha(230): "rgba(255,255,255,0.90)",
167 | props.ColorCurrentColor(): "currentColor",
168 | props.ColorInherit(): "inherit",
169 | props.ColorTransparent(): "transparent",
170 | {Keyword: "#efefef"}: "#efefef",
171 | {Keyword: "red"}: "red",
172 | {Color: color.Gray16{0}}: "rgba(0,0,0,1.00)",
173 | }
174 |
175 | for prop, expected := range testCases {
176 | st := &Style{Selector: ".test", Props: Props{BackgroundColor: prop}}
177 | css := fmt.Sprintf(".test{background-color:%s;}", expected)
178 | runTest(t, st, css)
179 | }
180 | }
181 |
182 | func TestStyle_BackgroundImage(t *testing.T) {
183 | testCases := map[props.BackgroundImage]string{
184 | props.BackgroundImageURL("image.jpg"): `url("image.jpg")`,
185 | props.BackgroundImageLinearGradient("red", "blue"): "linear-gradient(red,blue)",
186 | props.BackgroundImage("inherit"): "inherit",
187 | props.BackgroundImages(
188 | props.BackgroundImageLinearGradient("red", "blue"),
189 | props.BackgroundImageURL("image.jpg"),
190 | ): `linear-gradient(red,blue),url("image.jpg")`,
191 | }
192 |
193 | for prop, expected := range testCases {
194 | st := &Style{Selector: ".test", Props: Props{BackgroundImage: prop}}
195 | css := fmt.Sprintf(".test{background-image:%s;}", expected)
196 | runTest(t, st, css)
197 | }
198 | }
199 |
200 | func TestStyle_BackgroundPosition(t *testing.T) {
201 | testCases := map[props.BackgroundPosition]string{
202 | props.BackgroundPositionXY(props.UnitPx(10), props.UnitPx(20)): "10px 20px",
203 | props.BackgroundPositionEdges(
204 | props.BackgroundPositionEdge{Position: props.BackgroundPositionTop, Unit: props.UnitPx(10)},
205 | props.BackgroundPositionEdge{Position: props.BackgroundPositionRight, Unit: props.UnitPx(20)},
206 | ): "top 10px right 20px",
207 | props.BackgroundPositionTop: "top",
208 | props.BackgroundPositionBottom: "bottom",
209 | props.BackgroundPositionLeft: "left",
210 | props.BackgroundPositionRight: "right",
211 | props.BackgroundPositionCenter: "center",
212 | props.BackgroundPositionTopLeft: "top left",
213 | props.BackgroundPositionTopRight: "top right",
214 | props.BackgroundPositionBottomLeft: "bottom left",
215 | props.BackgroundPositionBottomRight: "bottom right",
216 | props.BackgroundPositions(
217 | props.BackgroundPositionTop,
218 | props.BackgroundPositionLeft,
219 | ): "top,left",
220 | props.BackgroundPosition("inherit"): "inherit",
221 | }
222 |
223 | for prop, expected := range testCases {
224 | st := &Style{Selector: ".test", Props: Props{BackgroundPosition: prop}}
225 | css := fmt.Sprintf(".test{background-position:%s;}", expected)
226 | runTest(t, st, css)
227 | }
228 | }
229 |
230 | func TestStyle_BackgroundRepeat(t *testing.T) {
231 | testCases := map[props.BackgroundRepeat]string{
232 | props.BackgroundRepeatRepeat: "repeat",
233 | props.BackgroundRepeatRepeatX: "repeat-x",
234 | props.BackgroundRepeatRepeatY: "repeat-y",
235 | props.BackgroundRepeatNoRepeat: "no-repeat",
236 | props.BackgroundRepeatSpace: "space",
237 | props.BackgroundRepeatRound: "round",
238 | props.BackgroundRepeats(
239 | props.BackgroundRepeatRepeat,
240 | props.BackgroundRepeatNoRepeat,
241 | ): "repeat,no-repeat",
242 | props.BackgroundRepeat("inherit"): "inherit",
243 | }
244 |
245 | for prop, expected := range testCases {
246 | st := &Style{Selector: ".test", Props: Props{BackgroundRepeat: prop}}
247 | css := fmt.Sprintf(".test{background-repeat:%s;}", expected)
248 | runTest(t, st, css)
249 | }
250 | }
251 |
252 | func TestStyle_BackgroundSize(t *testing.T) {
253 | testCases := map[props.BackgroundSize]string{
254 | props.BackgroundSizeWidth(props.UnitPx(10)): "10px",
255 | props.BackgroundSizeDimension(
256 | props.UnitPx(10),
257 | props.UnitPx(20),
258 | ): "10px 20px",
259 | props.BackgroundSizes(
260 | props.BackgroundSizeWidth(props.UnitPx(10)),
261 | props.BackgroundSizeDimension(props.UnitPx(10), props.UnitPx(20)),
262 | ): "10px,10px 20px",
263 | props.BackgroundSizeCover(): "cover",
264 | props.BackgroundSizeContain(): "contain",
265 | props.BackgroundSize("inherit"): "inherit",
266 | }
267 |
268 | for prop, expected := range testCases {
269 | st := &Style{Selector: ".test", Props: Props{BackgroundSize: prop}}
270 | css := fmt.Sprintf(".test{background-size:%s;}", expected)
271 | runTest(t, st, css)
272 | }
273 | }
274 |
275 | func TestStyle_Border(t *testing.T) {
276 | testCases := map[props.Border]string{
277 | {
278 | Width: props.UnitPx(10),
279 | Style: props.BorderStyleSolid,
280 | Color: props.ColorRGBA(0, 0, 0, 255),
281 | }: "10px solid rgba(0,0,0,1.00)",
282 | {
283 | Width: props.UnitPx(10),
284 | Style: props.BorderStyleDouble,
285 | Color: props.ColorRGBA(0, 0, 0, 255),
286 | }: "10px double rgba(0,0,0,1.00)",
287 | {
288 | Style: props.BorderStyleNone,
289 | }: "none",
290 | {
291 | Style: props.BorderStyle("initial"),
292 | }: "initial",
293 | }
294 |
295 | for prop, expected := range testCases {
296 | st := &Style{Selector: ".test", Props: Props{Border: prop}}
297 | css := fmt.Sprintf(".test{border:%s;}", expected)
298 | runTest(t, st, css)
299 | }
300 | }
301 |
302 | func TestStyle_BorderBottom(t *testing.T) {
303 | testCases := map[props.Border]string{
304 | {
305 | Width: props.UnitPx(10),
306 | Style: props.BorderStyleSolid,
307 | Color: props.ColorRGBA(0, 0, 0, 255),
308 | }: "10px solid rgba(0,0,0,1.00)",
309 | {
310 | Width: props.UnitPx(10),
311 | Style: props.BorderStyleDouble,
312 | Color: props.ColorRGBA(0, 0, 0, 255),
313 | }: "10px double rgba(0,0,0,1.00)",
314 | {
315 | Style: props.BorderStyleNone,
316 | }: "none",
317 | {
318 | Style: props.BorderStyle("initial"),
319 | }: "initial",
320 | }
321 |
322 | for prop, expected := range testCases {
323 | st := &Style{Selector: ".test", Props: Props{BorderBottom: prop}}
324 | css := fmt.Sprintf(".test{border-bottom:%s;}", expected)
325 | runTest(t, st, css)
326 | }
327 | }
328 |
329 | func TestStyle_BorderBottomLeftRadius(t *testing.T) {
330 | testCases := map[props.Unit]string{
331 | props.UnitPx(10): "10px",
332 | props.UnitPercent(50): "50.00%",
333 | props.UnitRem(10): "10.000rem",
334 | props.UnitRaw("20% 10%"): "20% 10%",
335 | }
336 |
337 | for prop, expected := range testCases {
338 | st := &Style{Selector: ".test", Props: Props{BorderBottomLeftRadius: prop}}
339 | css := fmt.Sprintf(".test{border-bottom-left-radius:%s;}", expected)
340 | runTest(t, st, css)
341 | }
342 | }
343 |
344 | func TestStyle_BorderBottomRightRadius(t *testing.T) {
345 | testCases := map[props.Unit]string{
346 | props.UnitPx(10): "10px",
347 | props.UnitPercent(50): "50.00%",
348 | props.UnitRem(10): "10.000rem",
349 | props.UnitRaw("20% 10%"): "20% 10%",
350 | }
351 |
352 | for prop, expected := range testCases {
353 | st := &Style{Selector: ".test", Props: Props{BorderBottomRightRadius: prop}}
354 | css := fmt.Sprintf(".test{border-bottom-right-radius:%s;}", expected)
355 | runTest(t, st, css)
356 | }
357 | }
358 |
359 | func TestStyle_BorderCollapse(t *testing.T) {
360 | testCases := map[props.BorderCollapse]string{
361 | props.BorderCollapseSeparate: "separate",
362 | props.BorderCollapseCollapse: "collapse",
363 | props.BorderCollapse("initial"): "initial",
364 | }
365 |
366 | for prop, expected := range testCases {
367 | st := &Style{Selector: ".test", Props: Props{BorderCollapse: prop}}
368 | css := fmt.Sprintf(".test{border-collapse:%s;}", expected)
369 | runTest(t, st, css)
370 | }
371 | }
372 |
373 | func TestStyle_BorderColor(t *testing.T) {
374 | testCases := map[props.Color]string{
375 | props.ColorRGBA(0, 0, 0, 255): "rgba(0,0,0,1.00)",
376 | props.ColorRGBA(255, 255, 255, 230): "rgba(255,255,255,0.90)",
377 | }
378 |
379 | for prop, expected := range testCases {
380 | st := &Style{Selector: ".test", Props: Props{BorderColor: prop}}
381 | css := fmt.Sprintf(".test{border-color:%s;}", expected)
382 | runTest(t, st, css)
383 | }
384 | }
385 |
386 | func TestStyle_BorderLeft(t *testing.T) {
387 | testCases := map[props.Border]string{
388 | {
389 | Width: props.UnitPx(10),
390 | Style: props.BorderStyleSolid,
391 | Color: props.ColorRGBA(0, 0, 0, 255),
392 | }: "10px solid rgba(0,0,0,1.00)",
393 | {
394 | Width: props.UnitPx(10),
395 | Style: props.BorderStyleDouble,
396 | Color: props.ColorRGBA(0, 0, 0, 255),
397 | }: "10px double rgba(0,0,0,1.00)",
398 | {
399 | Style: props.BorderStyleNone,
400 | }: "none",
401 | {
402 | Style: props.BorderStyle("initial"),
403 | }: "initial",
404 | }
405 |
406 | for prop, expected := range testCases {
407 | st := &Style{Selector: ".test", Props: Props{BorderLeft: prop}}
408 | css := fmt.Sprintf(".test{border-left:%s;}", expected)
409 | runTest(t, st, css)
410 | }
411 | }
412 |
413 | func TestStyle_BorderRadius(t *testing.T) {
414 | testCases := map[props.Unit]string{
415 | props.UnitPx(10): "10px",
416 | props.UnitPercent(50): "50.00%",
417 | props.UnitRem(10): "10.000rem",
418 | }
419 |
420 | for prop, expected := range testCases {
421 | st := &Style{Selector: ".test", Props: Props{BorderRadius: prop}}
422 | css := fmt.Sprintf(".test{border-radius:%s;}", expected)
423 | runTest(t, st, css)
424 | }
425 | }
426 |
427 | func TestStyle_BorderRight(t *testing.T) {
428 | testCases := map[props.Border]string{
429 | {
430 | Width: props.UnitPx(10),
431 | Style: props.BorderStyleSolid,
432 | Color: props.ColorRGBA(0, 0, 0, 255),
433 | }: "10px solid rgba(0,0,0,1.00)",
434 | {
435 | Width: props.UnitPx(5),
436 | Style: props.BorderStyleDouble,
437 | Color: props.ColorRGBA(0, 0, 0, 255),
438 | }: "5px double rgba(0,0,0,1.00)",
439 | {
440 | Style: props.BorderStyleNone,
441 | }: "none",
442 | {
443 | Style: props.BorderStyle("initial"),
444 | }: "initial",
445 | }
446 |
447 | for prop, expected := range testCases {
448 | st := &Style{Selector: ".test", Props: Props{BorderRight: prop}}
449 | css := fmt.Sprintf(".test{border-right:%s;}", expected)
450 | runTest(t, st, css)
451 | }
452 | }
453 |
454 | func TestStyle_BorderStyle(t *testing.T) {
455 | testCases := map[props.BorderStyle]string{
456 | props.BorderStyleNone: "none",
457 | props.BorderStyleHidden: "hidden",
458 | props.BorderStyleDotted: "dotted",
459 | props.BorderStyleDashed: "dashed",
460 | props.BorderStyleSolid: "solid",
461 | props.BorderStyleDouble: "double",
462 | props.BorderStyle("initial"): "initial",
463 | }
464 |
465 | for prop, expected := range testCases {
466 | st := &Style{Selector: ".test", Props: Props{BorderStyle: prop}}
467 | css := fmt.Sprintf(".test{border-style:%s;}", expected)
468 | runTest(t, st, css)
469 | }
470 | }
471 |
472 | func TestStyle_BorderTop(t *testing.T) {
473 | testCases := map[props.Border]string{
474 | {
475 | Width: props.UnitPx(10),
476 | Style: props.BorderStyleSolid,
477 | Color: props.ColorRGBA(0, 0, 0, 255),
478 | }: "10px solid rgba(0,0,0,1.00)",
479 | {
480 | Width: props.UnitPx(5),
481 | Style: props.BorderStyleDouble,
482 | Color: props.ColorRGBA(0, 0, 0, 255),
483 | }: "5px double rgba(0,0,0,1.00)",
484 | {
485 | Style: props.BorderStyleNone,
486 | }: "none",
487 | {
488 | Style: props.BorderStyle("initial"),
489 | }: "initial",
490 | }
491 |
492 | for prop, expected := range testCases {
493 | st := &Style{Selector: ".test", Props: Props{BorderTop: prop}}
494 | css := fmt.Sprintf(".test{border-top:%s;}", expected)
495 | runTest(t, st, css)
496 | }
497 | }
498 |
499 | func TestStyle_BorderTopLeftRadius(t *testing.T) {
500 | testCases := map[props.Unit]string{
501 | props.UnitPx(10): "10px",
502 | props.UnitPercent(50): "50.00%",
503 | props.UnitRem(10): "10.000rem",
504 | props.UnitRaw("20% 10%"): "20% 10%",
505 | }
506 |
507 | for prop, expected := range testCases {
508 | st := &Style{Selector: ".test", Props: Props{BorderTopLeftRadius: prop}}
509 | css := fmt.Sprintf(".test{border-top-left-radius:%s;}", expected)
510 | runTest(t, st, css)
511 | }
512 | }
513 |
514 | func TestStyle_BorderTopRightRadius(t *testing.T) {
515 | testCases := map[props.Unit]string{
516 | props.UnitPx(10): "10px",
517 | props.UnitPercent(50): "50.00%",
518 | props.UnitRem(10): "10.000rem",
519 | props.UnitRaw("20% 10%"): "20% 10%",
520 | }
521 |
522 | for prop, expected := range testCases {
523 | st := &Style{Selector: ".test", Props: Props{BorderTopRightRadius: prop}}
524 | css := fmt.Sprintf(".test{border-top-right-radius:%s;}", expected)
525 | runTest(t, st, css)
526 | }
527 | }
528 |
529 | func TestStyle_BorderWidth(t *testing.T) {
530 | testCases := map[props.Unit]string{
531 | props.UnitPx(10): "10px",
532 | props.UnitPercent(50): "50.00%",
533 | props.UnitRem(10): "10.000rem",
534 | }
535 |
536 | for prop, expected := range testCases {
537 | st := &Style{Selector: ".test", Props: Props{BorderWidth: prop}}
538 | css := fmt.Sprintf(".test{border-width:%s;}", expected)
539 | runTest(t, st, css)
540 | }
541 | }
542 |
543 | func TestStyle_Bottom(t *testing.T) {
544 | testCases := map[props.Unit]string{
545 | props.UnitPx(10): "10px",
546 | props.UnitPercent(50): "50.00%",
547 | props.UnitRem(10): "10.000rem",
548 | }
549 |
550 | for prop, expected := range testCases {
551 | st := &Style{Selector: ".test", Props: Props{Bottom: prop}}
552 | css := fmt.Sprintf(".test{bottom:%s;}", expected)
553 | runTest(t, st, css)
554 | }
555 | }
556 |
557 | func TestStyle_BoxSizing(t *testing.T) {
558 | testCases := map[props.BoxSizing]string{
559 | props.BoxSizingBorderBox: "border-box",
560 | props.BoxSizingContentBox: "content-box",
561 | props.BoxSizing("initial"): "initial",
562 | }
563 |
564 | for prop, expected := range testCases {
565 | st := &Style{Selector: ".test", Props: Props{BoxSizing: prop}}
566 | css := fmt.Sprintf(".test{box-sizing:%s;}", expected)
567 | runTest(t, st, css)
568 | }
569 | }
570 |
571 | func TestStyle_CaptionSide(t *testing.T) {
572 | testCases := map[props.CaptionSide]string{
573 | props.CaptionSideTop: "top",
574 | props.CaptionSideBottom: "bottom",
575 | props.CaptionSide("initial"): "initial",
576 | }
577 |
578 | for prop, expected := range testCases {
579 | st := &Style{Selector: ".test", Props: Props{CaptionSide: prop}}
580 | css := fmt.Sprintf(".test{caption-side:%s;}", expected)
581 | runTest(t, st, css)
582 | }
583 | }
584 |
585 | func TestStyle_Color(t *testing.T) {
586 | testCases := map[props.Color]string{
587 | props.ColorRGBA(0, 0, 0, 255): "rgba(0,0,0,1.00)",
588 | props.ColorRGBA(255, 255, 255, 230): "rgba(255,255,255,0.90)",
589 | props.ColorRGBA(255, 255, 255, 255).Alpha(230): "rgba(255,255,255,0.90)",
590 | props.ColorCurrentColor(): "currentColor",
591 | props.ColorInherit(): "inherit",
592 | props.ColorTransparent(): "transparent",
593 | {Keyword: "#efefef"}: "#efefef",
594 | {Keyword: "red"}: "red",
595 | {Color: color.Gray16{0}}: "rgba(0,0,0,1.00)",
596 | }
597 |
598 | for prop, expected := range testCases {
599 | st := &Style{Selector: ".test", Props: Props{Color: prop}}
600 | css := fmt.Sprintf(".test{color:%s;}", expected)
601 | runTest(t, st, css)
602 | }
603 | }
604 |
605 | func TestStyle_ColumnGap(t *testing.T) {
606 | testCases := map[props.Unit]string{
607 | props.UnitPx(10): "10px",
608 | props.UnitPercent(50): "50.00%",
609 | props.UnitRem(10): "10.000rem",
610 | }
611 |
612 | for prop, expected := range testCases {
613 | st := &Style{Selector: ".test", Props: Props{ColumnGap: prop}}
614 | css := fmt.Sprintf(".test{column-gap:%s;}", expected)
615 | runTest(t, st, css)
616 | }
617 | }
618 |
619 | func TestStyle_Cursor(t *testing.T) {
620 | testCases := map[props.Cursor]string{
621 | props.CursorAuto: "auto",
622 | props.CursorDefault: "default",
623 | props.CursorNone: "none",
624 | props.CursorHelp: "help",
625 | props.CursorPointer: "pointer",
626 | props.CursorWait: "wait",
627 | props.CursorText: "text",
628 | props.CursorMove: "move",
629 | props.CursorNotAllowed: "not-allowed",
630 | props.Cursor("inherit"): "inherit",
631 | }
632 |
633 | for prop, expected := range testCases {
634 | st := &Style{Selector: ".test", Props: Props{Cursor: prop}}
635 | css := fmt.Sprintf(".test{cursor:%s;}", expected)
636 | runTest(t, st, css)
637 | }
638 | }
639 |
640 | func TestStyle_Display(t *testing.T) {
641 | testCases := map[props.Display]string{
642 | props.DisplayBlock: "block",
643 | props.DisplayInline: "inline",
644 | props.DisplayInlineBlock: "inline-block",
645 | props.DisplayInlineFlex: "inline-flex",
646 | props.DisplayInlineGrid: "inline-grid",
647 | props.DisplayFlex: "flex",
648 | props.DisplayGrid: "grid",
649 | props.DisplayNone: "none",
650 | props.DisplayTable: "table",
651 | props.DisplayInlineTable: "inline-table",
652 | props.DisplayTableCaption: "table-caption",
653 | props.DisplayTableCell: "table-cell",
654 | props.DisplayTableColumn: "table-column",
655 | props.DisplayTableColumnGroup: "table-column-group",
656 | props.DisplayTableFooterGroup: "table-footer-group",
657 | props.DisplayTableHeaderGroup: "table-header-group",
658 | props.DisplayTableRowGroup: "table-row-group",
659 | props.DisplayTableRow: "table-row",
660 | props.DisplayFlowRoot: "flow-root",
661 | props.DisplayContents: "contents",
662 | props.DisplayListItem: "list-item",
663 | props.Display("inherit"): "inherit",
664 | }
665 |
666 | for prop, expected := range testCases {
667 | st := &Style{Selector: ".test", Props: Props{Display: prop}}
668 | css := fmt.Sprintf(".test{display:%s;}", expected)
669 | runTest(t, st, css)
670 | }
671 | }
672 |
673 | func TestStyle_FlexBasis(t *testing.T) {
674 | testCases := map[props.Unit]string{
675 | props.UnitPx(10): "10px",
676 | props.UnitAuto(): "auto",
677 | props.UnitRaw(0): "0",
678 | }
679 |
680 | for prop, expected := range testCases {
681 | st := &Style{Selector: ".test", Props: Props{FlexBasis: prop}}
682 | css := fmt.Sprintf(".test{flex-basis:%s;}", expected)
683 | runTest(t, st, css)
684 | }
685 | }
686 |
687 | func TestStyle_FlexDirection(t *testing.T) {
688 | testCases := map[props.FlexDirection]string{
689 | props.FlexDirectionRow: "row",
690 | props.FlexDirectionRowReverse: "row-reverse",
691 | props.FlexDirectionColumn: "column",
692 | props.FlexDirectionColumnReverse: "column-reverse",
693 | props.FlexDirection("initial"): "initial",
694 | }
695 |
696 | for prop, expected := range testCases {
697 | st := &Style{Selector: ".test", Props: Props{FlexDirection: prop}}
698 | css := fmt.Sprintf(".test{flex-direction:%s;}", expected)
699 | runTest(t, st, css)
700 | }
701 | }
702 |
703 | func TestStyle_FlexGrow(t *testing.T) {
704 | testCases := map[props.Unit]string{
705 | props.UnitRaw(1): "1",
706 | props.UnitRaw(0): "0",
707 | props.UnitRaw("initial"): "initial",
708 | }
709 |
710 | for prop, expected := range testCases {
711 | st := &Style{Selector: ".test", Props: Props{FlexGrow: prop}}
712 | css := fmt.Sprintf(".test{flex-grow:%s;}", expected)
713 | runTest(t, st, css)
714 | }
715 | }
716 |
717 | func TestStyle_FlexShrink(t *testing.T) {
718 | testCases := map[props.Unit]string{
719 | props.UnitRaw(1): "1",
720 | props.UnitRaw(0): "0",
721 | props.UnitRaw("initial"): "initial",
722 | }
723 |
724 | for prop, expected := range testCases {
725 | st := &Style{Selector: ".test", Props: Props{FlexShrink: prop}}
726 | css := fmt.Sprintf(".test{flex-shrink:%s;}", expected)
727 | runTest(t, st, css)
728 | }
729 | }
730 |
731 | func TestStyle_FlexWrap(t *testing.T) {
732 | testCases := map[props.FlexWrap]string{
733 | props.FlexWrapNoWrap: "nowrap",
734 | props.FlexWrapWrap: "wrap",
735 | props.FlexWrapWrapReverse: "wrap-reverse",
736 | props.FlexWrap("initial"): "initial",
737 | }
738 |
739 | for prop, expected := range testCases {
740 | st := &Style{Selector: ".test", Props: Props{FlexWrap: prop}}
741 | css := fmt.Sprintf(".test{flex-wrap:%s;}", expected)
742 | runTest(t, st, css)
743 | }
744 | }
745 |
746 | func TestStyle_Float(t *testing.T) {
747 | testCases := map[props.Float]string{
748 | props.FloatLeft: "left",
749 | props.FloatRight: "right",
750 | props.FloatNone: "none",
751 | props.Float("initial"): "initial",
752 | }
753 |
754 | for prop, expected := range testCases {
755 | st := &Style{Selector: ".test", Props: Props{Float: prop}}
756 | css := fmt.Sprintf(".test{float:%s;}", expected)
757 | runTest(t, st, css)
758 | }
759 | }
760 |
761 | func TestStyle_FontFamily(t *testing.T) {
762 | testCases := map[props.FontFamily]string{
763 | props.FontFamilySans: "ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"",
764 | props.FontFamilySerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif",
765 | props.FontFamilyMono: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace",
766 | props.FontFamily("\"Gill Sans Extrabold\", sans-serif"): "\"Gill Sans Extrabold\", sans-serif",
767 | props.FontFamily("sans-serif"): "sans-serif",
768 | }
769 |
770 | for prop, expected := range testCases {
771 | st := &Style{Selector: ".test", Props: Props{FontFamily: prop}}
772 | css := fmt.Sprintf(".test{font-family:%s;}", expected)
773 | runTest(t, st, css)
774 | }
775 |
776 | }
777 | func TestStyle_FontSize(t *testing.T) {
778 | testCases := map[props.Unit]string{
779 | props.UnitPx(20): "20px",
780 | props.UnitRem(2): "2.000rem",
781 | }
782 |
783 | for prop, expected := range testCases {
784 | st := &Style{Selector: ".test", Props: Props{FontSize: prop}}
785 | css := fmt.Sprintf(".test{font-size:%s;}", expected)
786 | runTest(t, st, css)
787 | }
788 | }
789 |
790 | func TestStyle_FontStyle(t *testing.T) {
791 | testCases := map[props.FontStyle]string{
792 | props.FontStyleNormal: "normal",
793 | props.FontStyleItalic: "italic",
794 | props.FontStyle("initial"): "initial",
795 | }
796 |
797 | for prop, expected := range testCases {
798 | st := &Style{Selector: ".test", Props: Props{FontStyle: prop}}
799 | css := fmt.Sprintf(".test{font-style:%s;}", expected)
800 | runTest(t, st, css)
801 | }
802 | }
803 |
804 | func TestStyle_FontWeight(t *testing.T) {
805 | testCases := map[props.FontWeight]string{
806 | props.FontWeightThin: "100",
807 | props.FontWeightExtraLight: "200",
808 | props.FontWeightLight: "300",
809 | props.FontWeightNormal: "400",
810 | props.FontWeightMedium: "500",
811 | props.FontWeightSemiBold: "600",
812 | props.FontWeightBold: "700",
813 | props.FontWeightExtraBold: "800",
814 | props.FontWeightBlack: "900",
815 | props.FontWeight("initial"): "initial",
816 | }
817 |
818 | for prop, expected := range testCases {
819 | st := &Style{Selector: ".test", Props: Props{FontWeight: prop}}
820 | css := fmt.Sprintf(".test{font-weight:%s;}", expected)
821 | runTest(t, st, css)
822 | }
823 | }
824 |
825 | func TestStyle_Gap(t *testing.T) {
826 | testCases := map[props.Unit]string{
827 | props.UnitPx(10): "10px",
828 | props.UnitPercent(50): "50.00%",
829 | props.UnitRem(10): "10.000rem",
830 | }
831 |
832 | for prop, expected := range testCases {
833 | st := &Style{Selector: ".test", Props: Props{Gap: prop}}
834 | css := fmt.Sprintf(".test{gap:%s;}", expected)
835 | runTest(t, st, css)
836 | }
837 | }
838 |
839 | func TestStyle_Height(t *testing.T) {
840 | testCases := map[props.Unit]string{
841 | props.UnitPx(20): "20px",
842 | props.UnitRem(2): "2.000rem",
843 | props.UnitPercent(50): "50.00%",
844 | props.UnitAuto(): "auto",
845 | props.UnitRaw(0): "0",
846 | }
847 |
848 | for prop, expected := range testCases {
849 | st := &Style{Selector: ".test", Props: Props{Height: prop}}
850 | css := fmt.Sprintf(".test{height:%s;}", expected)
851 | runTest(t, st, css)
852 | }
853 | }
854 |
855 | func TestStyle_JustifyContent(t *testing.T) {
856 | testCases := map[props.JustifyContent]string{
857 | props.JustifyContentNormal: "normal",
858 | props.JustifyContentCenter: "center",
859 | props.JustifyContentStart: "start",
860 | props.JustifyContentEnd: "end",
861 | props.JustifyContentFlexStart: "flex-start",
862 | props.JustifyContentFlexEnd: "flex-end",
863 | props.JustifyContentLeft: "left",
864 | props.JustifyContentRight: "right",
865 | props.JustifyContentSpaceBetween: "space-between",
866 | props.JustifyContentSpaceAround: "space-around",
867 | props.JustifyContentSpaceEvenly: "space-evenly",
868 | props.JustifyContentStretch: "stretch",
869 | props.JustifyContent("initial"): "initial",
870 | }
871 |
872 | for prop, expected := range testCases {
873 | st := &Style{Selector: ".test", Props: Props{JustifyContent: prop}}
874 | css := fmt.Sprintf(".test{justify-content:%s;}", expected)
875 | runTest(t, st, css)
876 | }
877 | }
878 |
879 | func TestStyle_JustifyItems(t *testing.T) {
880 | testCases := map[props.JustifyItems]string{
881 | props.JustifyItemsNormal: "normal",
882 | props.JustifyItemsStretch: "stretch",
883 | props.JustifyItemsCenter: "center",
884 | props.JustifyItemsStart: "start",
885 | props.JustifyItemsEnd: "end",
886 | props.JustifyItemsFlexStart: "flex-start",
887 | props.JustifyItemsFlexEnd: "flex-end",
888 | props.JustifyItemsSelfStart: "self-start",
889 | props.JustifyItemsSelfEnd: "self-end",
890 | props.JustifyItemsLeft: "left",
891 | props.JustifyItemsRight: "right",
892 | props.JustifyItemsBaseline: "baseline",
893 | props.JustifyItems("inherit"): "inherit",
894 | }
895 |
896 | for prop, expected := range testCases {
897 | st := &Style{Selector: ".test", Props: Props{JustifyItems: prop}}
898 | css := fmt.Sprintf(".test{justify-items:%s;}", expected)
899 | runTest(t, st, css)
900 | }
901 | }
902 |
903 | func TestStyle_JustifySelf(t *testing.T) {
904 | testCases := map[props.JustifySelf]string{
905 | props.JustifySelfAuto: "auto",
906 | props.JustifySelfNormal: "normal",
907 | props.JustifySelfStretch: "stretch",
908 | props.JustifySelfCenter: "center",
909 | props.JustifySelfStart: "start",
910 | props.JustifySelfEnd: "end",
911 | props.JustifySelfFlexStart: "flex-start",
912 | props.JustifySelfFlexEnd: "flex-end",
913 | props.JustifySelfSelfStart: "self-start",
914 | props.JustifySelfSelfEnd: "self-end",
915 | props.JustifySelfLeft: "left",
916 | props.JustifySelfRight: "right",
917 | props.JustifySelfBaseline: "baseline",
918 | props.JustifySelf("initial"): "initial",
919 | }
920 |
921 | for prop, expected := range testCases {
922 | st := &Style{Selector: ".test", Props: Props{JustifySelf: prop}}
923 | css := fmt.Sprintf(".test{justify-self:%s;}", expected)
924 | runTest(t, st, css)
925 | }
926 | }
927 |
928 | func TestStyle_Left(t *testing.T) {
929 | testCases := map[props.Unit]string{
930 | props.UnitPx(10): "10px",
931 | props.UnitPercent(50): "50.00%",
932 | props.UnitRem(10): "10.000rem",
933 | }
934 |
935 | for prop, expected := range testCases {
936 | st := &Style{Selector: ".test", Props: Props{Left: prop}}
937 | css := fmt.Sprintf(".test{left:%s;}", expected)
938 | runTest(t, st, css)
939 | }
940 | }
941 |
942 | func TestStyle_ListStylePosition(t *testing.T) {
943 | testCases := map[props.ListStylePosition]string{
944 | props.ListStylePositionInside: "inside",
945 | props.ListStylePositionOutside: "outside",
946 | }
947 |
948 | for prop, expected := range testCases {
949 | st := &Style{Selector: ".test", Props: Props{ListStylePosition: prop}}
950 | css := fmt.Sprintf(".test{list-style-position:%s;}", expected)
951 | runTest(t, st, css)
952 | }
953 | }
954 |
955 | func TestStyle_ListStyleType(t *testing.T) {
956 | testCases := map[props.ListStyleType]string{
957 | props.ListStyleTypeNone: "none",
958 | props.ListStyleTypeDisc: "disc",
959 | props.ListStyleTypeDecimal: "decimal",
960 | }
961 |
962 | for prop, expected := range testCases {
963 | st := &Style{Selector: ".test", Props: Props{ListStyleType: prop}}
964 | css := fmt.Sprintf(".test{list-style-type:%s;}", expected)
965 | runTest(t, st, css)
966 | }
967 | }
968 |
969 | func TestStyle_LineHeight(t *testing.T) {
970 | testCases := map[props.Unit]string{
971 | props.UnitRaw(1.5): "1.5",
972 | props.UnitPx(20): "20px",
973 | props.UnitEm(3): "3.000em",
974 | }
975 |
976 | for prop, expected := range testCases {
977 | st := &Style{Selector: ".test", Props: Props{LineHeight: prop}}
978 | css := fmt.Sprintf(".test{line-height:%s;}", expected)
979 | runTest(t, st, css)
980 | }
981 | }
982 |
983 | func TestStyle_Margin(t *testing.T) {
984 | testCases := map[props.Unit]string{
985 | props.UnitPx(10): "10px",
986 | props.UnitRem(2): "2.000rem",
987 | props.UnitAuto(): "auto",
988 | }
989 |
990 | for prop, expected := range testCases {
991 | st := &Style{Selector: ".test", Props: Props{Margin: prop}}
992 | css := fmt.Sprintf(".test{margin:%s;}", expected)
993 | runTest(t, st, css)
994 | }
995 | }
996 |
997 | func TestStyle_MarginBottom(t *testing.T) {
998 | testCases := map[props.Unit]string{
999 | props.UnitPx(10): "10px",
1000 | props.UnitRem(2): "2.000rem",
1001 | props.UnitAuto(): "auto",
1002 | }
1003 |
1004 | for prop, expected := range testCases {
1005 | st := &Style{Selector: ".test", Props: Props{MarginBottom: prop}}
1006 | css := fmt.Sprintf(".test{margin-bottom:%s;}", expected)
1007 | runTest(t, st, css)
1008 | }
1009 | }
1010 |
1011 | func TestStyle_MarginLeft(t *testing.T) {
1012 | testCases := map[props.Unit]string{
1013 | props.UnitPx(10): "10px",
1014 | props.UnitRem(2): "2.000rem",
1015 | props.UnitAuto(): "auto",
1016 | }
1017 |
1018 | for prop, expected := range testCases {
1019 | st := &Style{Selector: ".test", Props: Props{MarginLeft: prop}}
1020 | css := fmt.Sprintf(".test{margin-left:%s;}", expected)
1021 | runTest(t, st, css)
1022 | }
1023 | }
1024 |
1025 | func TestStyle_MarginRight(t *testing.T) {
1026 | testCases := map[props.Unit]string{
1027 | props.UnitPx(10): "10px",
1028 | props.UnitRem(2): "2.000rem",
1029 | props.UnitAuto(): "auto",
1030 | }
1031 |
1032 | for prop, expected := range testCases {
1033 | st := &Style{Selector: ".test", Props: Props{MarginRight: prop}}
1034 | css := fmt.Sprintf(".test{margin-right:%s;}", expected)
1035 | runTest(t, st, css)
1036 | }
1037 | }
1038 |
1039 | func TestStyle_MarginTop(t *testing.T) {
1040 | testCases := map[props.Unit]string{
1041 | props.UnitPx(10): "10px",
1042 | props.UnitRem(2): "2.000rem",
1043 | props.UnitAuto(): "auto",
1044 | }
1045 |
1046 | for prop, expected := range testCases {
1047 | st := &Style{Selector: ".test", Props: Props{MarginTop: prop}}
1048 | css := fmt.Sprintf(".test{margin-top:%s;}", expected)
1049 | runTest(t, st, css)
1050 | }
1051 | }
1052 |
1053 | func TestStyle_MaxHeight(t *testing.T) {
1054 | testCases := map[props.Unit]string{
1055 | props.UnitPx(10): "10px",
1056 | props.UnitRem(2): "2.000rem",
1057 | props.UnitVh(100): "100vh",
1058 | props.UnitAuto(): "auto",
1059 | }
1060 |
1061 | for prop, expected := range testCases {
1062 | st := &Style{Selector: ".test", Props: Props{MaxHeight: prop}}
1063 | css := fmt.Sprintf(".test{max-height:%s;}", expected)
1064 | runTest(t, st, css)
1065 | }
1066 | }
1067 |
1068 | func TestStyle_MaxWidth(t *testing.T) {
1069 | testCases := map[props.Unit]string{
1070 | props.UnitPx(10): "10px",
1071 | props.UnitRem(2): "2.000rem",
1072 | props.UnitVw(100): "100vw",
1073 | props.UnitAuto(): "auto",
1074 | }
1075 |
1076 | for prop, expected := range testCases {
1077 | st := &Style{Selector: ".test", Props: Props{MaxWidth: prop}}
1078 | css := fmt.Sprintf(".test{max-width:%s;}", expected)
1079 | runTest(t, st, css)
1080 | }
1081 | }
1082 |
1083 | func TestStyle_MinHeight(t *testing.T) {
1084 | testCases := map[props.Unit]string{
1085 | props.UnitPx(10): "10px",
1086 | props.UnitRem(2): "2.000rem",
1087 | props.UnitVh(100): "100vh",
1088 | props.UnitAuto(): "auto",
1089 | }
1090 |
1091 | for prop, expected := range testCases {
1092 | st := &Style{Selector: ".test", Props: Props{MinHeight: prop}}
1093 | css := fmt.Sprintf(".test{min-height:%s;}", expected)
1094 | runTest(t, st, css)
1095 | }
1096 | }
1097 |
1098 | func TestStyle_MinWidth(t *testing.T) {
1099 | testCases := map[props.Unit]string{
1100 | props.UnitPx(10): "10px",
1101 | props.UnitRem(2): "2.000rem",
1102 | props.UnitVw(100): "100vw",
1103 | props.UnitAuto(): "auto",
1104 | }
1105 |
1106 | for prop, expected := range testCases {
1107 | st := &Style{Selector: ".test", Props: Props{MinWidth: prop}}
1108 | css := fmt.Sprintf(".test{min-width:%s;}", expected)
1109 | runTest(t, st, css)
1110 | }
1111 | }
1112 |
1113 | func TestStyle_Opacity(t *testing.T) {
1114 | testCases := map[props.Unit]string{
1115 | props.UnitRaw(0): "0",
1116 | props.UnitRaw(0.33): "0.33",
1117 | props.UnitPercent(90): "90.00%",
1118 | props.UnitRaw("initial"): "initial",
1119 | }
1120 |
1121 | for prop, expected := range testCases {
1122 | st := &Style{Selector: ".test", Props: Props{Opacity: prop}}
1123 | css := fmt.Sprintf(".test{opacity:%s;}", expected)
1124 | runTest(t, st, css)
1125 | }
1126 | }
1127 |
1128 | func TestStyle_Overflow(t *testing.T) {
1129 | testCases := map[props.Overflow]string{
1130 | props.OverflowVisible: "visible",
1131 | props.OverflowHidden: "hidden",
1132 | props.OverflowScroll: "scroll",
1133 | props.OverflowAuto: "auto",
1134 | props.OverflowClip: "clip",
1135 | props.Overflow("initial"): "initial",
1136 | }
1137 |
1138 | for prop, expected := range testCases {
1139 | st := &Style{Selector: ".test", Props: Props{Overflow: prop}}
1140 | css := fmt.Sprintf(".test{overflow:%s;}", expected)
1141 | runTest(t, st, css)
1142 | }
1143 | }
1144 |
1145 | func TestStyle_OverflowX(t *testing.T) {
1146 | testCases := map[props.Overflow]string{
1147 | props.OverflowVisible: "visible",
1148 | props.OverflowHidden: "hidden",
1149 | props.OverflowScroll: "scroll",
1150 | props.OverflowAuto: "auto",
1151 | props.OverflowClip: "clip",
1152 | props.Overflow("initial"): "initial",
1153 | }
1154 |
1155 | for prop, expected := range testCases {
1156 | st := &Style{Selector: ".test", Props: Props{OverflowX: prop}}
1157 | css := fmt.Sprintf(".test{overflow-x:%s;}", expected)
1158 | runTest(t, st, css)
1159 | }
1160 | }
1161 |
1162 | func TestStyle_OverflowY(t *testing.T) {
1163 | testCases := map[props.Overflow]string{
1164 | props.OverflowVisible: "visible",
1165 | props.OverflowHidden: "hidden",
1166 | props.OverflowScroll: "scroll",
1167 | props.OverflowAuto: "auto",
1168 | props.OverflowClip: "clip",
1169 | props.Overflow("initial"): "initial",
1170 | }
1171 |
1172 | for prop, expected := range testCases {
1173 | st := &Style{Selector: ".test", Props: Props{OverflowY: prop}}
1174 | css := fmt.Sprintf(".test{overflow-y:%s;}", expected)
1175 | runTest(t, st, css)
1176 | }
1177 | }
1178 |
1179 | func TestStyle_Padding(t *testing.T) {
1180 | testCases := map[props.Unit]string{
1181 | props.UnitPx(10): "10px",
1182 | props.UnitRem(2): "2.000rem",
1183 | props.UnitAuto(): "auto",
1184 | props.UnitRaw(0): "0",
1185 | }
1186 |
1187 | for prop, expected := range testCases {
1188 | st := &Style{Selector: ".test", Props: Props{Padding: prop}}
1189 | css := fmt.Sprintf(".test{padding:%s;}", expected)
1190 | runTest(t, st, css)
1191 | }
1192 | }
1193 |
1194 | func TestStyle_PaddingBottom(t *testing.T) {
1195 | testCases := map[props.Unit]string{
1196 | props.UnitPx(10): "10px",
1197 | props.UnitRem(2): "2.000rem",
1198 | props.UnitAuto(): "auto",
1199 | props.UnitRaw(0): "0",
1200 | }
1201 |
1202 | for prop, expected := range testCases {
1203 | st := &Style{Selector: ".test", Props: Props{PaddingBottom: prop}}
1204 | css := fmt.Sprintf(".test{padding-bottom:%s;}", expected)
1205 | runTest(t, st, css)
1206 | }
1207 | }
1208 |
1209 | func TestStyle_PaddingLeft(t *testing.T) {
1210 | testCases := map[props.Unit]string{
1211 | props.UnitPx(10): "10px",
1212 | props.UnitRem(2): "2.000rem",
1213 | props.UnitAuto(): "auto",
1214 | props.UnitRaw(0): "0",
1215 | }
1216 |
1217 | for prop, expected := range testCases {
1218 | st := &Style{Selector: ".test", Props: Props{PaddingLeft: prop}}
1219 | css := fmt.Sprintf(".test{padding-left:%s;}", expected)
1220 | runTest(t, st, css)
1221 | }
1222 | }
1223 |
1224 | func TestStyle_PaddingRight(t *testing.T) {
1225 | testCases := map[props.Unit]string{
1226 | props.UnitPx(10): "10px",
1227 | props.UnitRem(2): "2.000rem",
1228 | props.UnitAuto(): "auto",
1229 | props.UnitRaw(0): "0",
1230 | }
1231 |
1232 | for prop, expected := range testCases {
1233 | st := &Style{Selector: ".test", Props: Props{PaddingRight: prop}}
1234 | css := fmt.Sprintf(".test{padding-right:%s;}", expected)
1235 | runTest(t, st, css)
1236 | }
1237 | }
1238 |
1239 | func TestStyle_PaddingTop(t *testing.T) {
1240 | testCases := map[props.Unit]string{
1241 | props.UnitPx(10): "10px",
1242 | props.UnitRem(2): "2.000rem",
1243 | props.UnitAuto(): "auto",
1244 | props.UnitRaw(0): "0",
1245 | }
1246 |
1247 | for prop, expected := range testCases {
1248 | st := &Style{Selector: ".test", Props: Props{PaddingTop: prop}}
1249 | css := fmt.Sprintf(".test{padding-top:%s;}", expected)
1250 | runTest(t, st, css)
1251 | }
1252 | }
1253 |
1254 | func TestStyle_Position(t *testing.T) {
1255 | testCases := map[props.Position]string{
1256 | props.PositionStatic: "static",
1257 | props.PositionRelative: "relative",
1258 | props.PositionAbsolute: "absolute",
1259 | props.PositionFixed: "fixed",
1260 | props.PositionSticky: "sticky",
1261 | props.Position("initial"): "initial",
1262 | }
1263 |
1264 | for prop, expected := range testCases {
1265 | st := &Style{Selector: ".test", Props: Props{Position: prop}}
1266 | css := fmt.Sprintf(".test{position:%s;}", expected)
1267 | runTest(t, st, css)
1268 | }
1269 | }
1270 |
1271 | func TestStyle_PrintColorAdjust(t *testing.T) {
1272 | testCases := map[props.PrintColorAdjust]string{
1273 | props.PrintColorAdjustEconomy: "economy",
1274 | props.PrintColorAdjustExact: "exact",
1275 | props.PrintColorAdjust("auto"): "auto",
1276 | }
1277 |
1278 | for prop, expected := range testCases {
1279 | st := &Style{Selector: ".test", Props: Props{PrintColorAdjust: prop}}
1280 | css := fmt.Sprintf(".test{print-color-adjust:%s;}", expected)
1281 | runTest(t, st, css)
1282 | }
1283 | }
1284 |
1285 | func TestStyle_Right(t *testing.T) {
1286 | testCases := map[props.Unit]string{
1287 | props.UnitPx(10): "10px",
1288 | props.UnitPercent(50): "50.00%",
1289 | props.UnitRem(10): "10.000rem",
1290 | }
1291 |
1292 | for prop, expected := range testCases {
1293 | st := &Style{Selector: ".test", Props: Props{Right: prop}}
1294 | css := fmt.Sprintf(".test{right:%s;}", expected)
1295 | runTest(t, st, css)
1296 | }
1297 | }
1298 |
1299 | func TestStyle_RowGap(t *testing.T) {
1300 | testCases := map[props.Unit]string{
1301 | props.UnitPx(10): "10px",
1302 | props.UnitPercent(50): "50.00%",
1303 | props.UnitRem(10): "10.000rem",
1304 | }
1305 |
1306 | for prop, expected := range testCases {
1307 | st := &Style{Selector: ".test", Props: Props{RowGap: prop}}
1308 | css := fmt.Sprintf(".test{row-gap:%s;}", expected)
1309 | runTest(t, st, css)
1310 | }
1311 | }
1312 |
1313 | func TestStyle_TextAlign(t *testing.T) {
1314 | testCases := map[props.TextAlign]string{
1315 | props.TextAlignLeft: "left",
1316 | props.TextAlignRight: "right",
1317 | props.TextAlignCenter: "center",
1318 | props.TextAlignJustify: "justify",
1319 | props.TextAlignStart: "start",
1320 | props.TextAlignEnd: "end",
1321 | props.TextAlign("initial"): "initial",
1322 | }
1323 |
1324 | for prop, expected := range testCases {
1325 | st := &Style{Selector: ".test", Props: Props{TextAlign: prop}}
1326 | css := fmt.Sprintf(".test{text-align:%s;}", expected)
1327 | runTest(t, st, css)
1328 | }
1329 | }
1330 |
1331 | func TestStyle_TextDecorationColor(t *testing.T) {
1332 | testCases := map[props.Color]string{
1333 | props.ColorRGBA(0, 0, 0, 255): "rgba(0,0,0,1.00)",
1334 | props.ColorRGBA(255, 255, 255, 230): "rgba(255,255,255,0.90)",
1335 | props.ColorRGBA(255, 255, 255, 255).Alpha(230): "rgba(255,255,255,0.90)",
1336 | props.ColorCurrentColor(): "currentColor",
1337 | }
1338 |
1339 | for prop, expected := range testCases {
1340 | st := &Style{Selector: ".test", Props: Props{TextDecorationColor: prop}}
1341 | css := fmt.Sprintf(".test{text-decoration-color:%s;}", expected)
1342 | runTest(t, st, css)
1343 | }
1344 | }
1345 |
1346 | func TestStyle_TextDecorationLine(t *testing.T) {
1347 | testCases := map[props.TextDecorationLine]string{
1348 | props.TextDecorationLineNone: "none",
1349 | props.TextDecorationLineUnderline: "underline",
1350 | props.TextDecorationLineOverline: "overline",
1351 | props.TextDecorationLineLineThrough: "line-through",
1352 | props.TextDecorationLine("initial"): "initial",
1353 | }
1354 |
1355 | for prop, expected := range testCases {
1356 | st := &Style{Selector: ".test", Props: Props{TextDecorationLine: prop}}
1357 | css := fmt.Sprintf(".test{text-decoration-line:%s;}", expected)
1358 | runTest(t, st, css)
1359 | }
1360 | }
1361 |
1362 | func TestStyle_TextDecorationStyle(t *testing.T) {
1363 | testCases := map[props.TextDecorationStyle]string{
1364 | props.TextDecorationStyleSolid: "solid",
1365 | props.TextDecorationStyleDouble: "double",
1366 | props.TextDecorationStyleDotted: "dotted",
1367 | props.TextDecorationStyleDashed: "dashed",
1368 | props.TextDecorationStyleWavy: "wavy",
1369 | props.TextDecorationStyle("initial"): "initial",
1370 | }
1371 |
1372 | for prop, expected := range testCases {
1373 | st := &Style{Selector: ".test", Props: Props{TextDecorationStyle: prop}}
1374 | css := fmt.Sprintf(".test{text-decoration-style:%s;}", expected)
1375 | runTest(t, st, css)
1376 | }
1377 | }
1378 |
1379 | func TestStyle_TextDecorationThickness(t *testing.T) {
1380 | testCases := map[props.Unit]string{
1381 | props.UnitPx(1): "1px",
1382 | props.UnitAuto(): "auto",
1383 | props.UnitRaw("from-font"): "from-font",
1384 | props.UnitInherit(): "inherit",
1385 | }
1386 |
1387 | for prop, expected := range testCases {
1388 | st := &Style{Selector: ".test", Props: Props{TextDecorationThickness: prop}}
1389 | css := fmt.Sprintf(".test{text-decoration-thickness:%s;}", expected)
1390 | runTest(t, st, css)
1391 | }
1392 | }
1393 |
1394 | func TestStyle_TextIndent(t *testing.T) {
1395 | testCases := map[props.Unit]string{
1396 | props.UnitPx(0): "0px",
1397 | props.UnitRem(0.875): "0.875rem",
1398 | props.UnitInherit(): "inherit",
1399 | }
1400 |
1401 | for prop, expected := range testCases {
1402 | st := &Style{Selector: ".test", Props: Props{TextIndent: prop}}
1403 | css := fmt.Sprintf(".test{text-indent:%s;}", expected)
1404 | runTest(t, st, css)
1405 | }
1406 | }
1407 |
1408 | func TestStyle_TextUnderlineOffset(t *testing.T) {
1409 | testCases := map[props.Unit]string{
1410 | props.UnitPx(1): "1px",
1411 | props.UnitAuto(): "auto",
1412 | }
1413 |
1414 | for prop, expected := range testCases {
1415 | st := &Style{Selector: ".test", Props: Props{TextUnderlineOffset: prop}}
1416 | css := fmt.Sprintf(".test{text-underline-offset:%s;}", expected)
1417 | runTest(t, st, css)
1418 | }
1419 | }
1420 |
1421 | func TestStyle_TextOverflow(t *testing.T) {
1422 | testCases := map[props.TextOverflow]string{
1423 | props.TextOverflowClip: "clip",
1424 | props.TextOverflowEllipsis: "ellipsis",
1425 | props.TextOverflow("initial"): "initial",
1426 | }
1427 |
1428 | for prop, expected := range testCases {
1429 | st := &Style{Selector: ".test", Props: Props{TextOverflow: prop}}
1430 | css := fmt.Sprintf(".test{text-overflow:%s;}", expected)
1431 | runTest(t, st, css)
1432 | }
1433 | }
1434 |
1435 | func TestStyle_TextTransform(t *testing.T) {
1436 | testCases := map[props.TextTransform]string{
1437 | props.TextTransformNone: "none",
1438 | props.TextTransformCapitalize: "capitalize",
1439 | props.TextTransformUppercase: "uppercase",
1440 | props.TextTransformLowercase: "lowercase",
1441 | props.TextTransform("initial"): "initial",
1442 | }
1443 |
1444 | for prop, expected := range testCases {
1445 | st := &Style{Selector: ".test", Props: Props{TextTransform: prop}}
1446 | css := fmt.Sprintf(".test{text-transform:%s;}", expected)
1447 | runTest(t, st, css)
1448 | }
1449 | }
1450 |
1451 | func TestStyle_TextWrap(t *testing.T) {
1452 | testCases := map[props.TextWrap]string{
1453 | props.TextWrapWrap: "wrap",
1454 | props.TextWrapNoWrap: "nowrap",
1455 | props.TextWrapBalance: "balance",
1456 | props.TextWrap("inherit"): "inherit",
1457 | }
1458 |
1459 | for prop, expected := range testCases {
1460 | st := &Style{Selector: ".test", Props: Props{TextWrap: prop}}
1461 | css := fmt.Sprintf(".test{text-wrap:%s;}", expected)
1462 | runTest(t, st, css)
1463 | }
1464 | }
1465 |
1466 | func TestStyle_Top(t *testing.T) {
1467 | testCases := map[props.Unit]string{
1468 | props.UnitPx(10): "10px",
1469 | props.UnitPercent(50): "50.00%",
1470 | props.UnitRem(10): "10.000rem",
1471 | }
1472 |
1473 | for prop, expected := range testCases {
1474 | st := &Style{Selector: ".test", Props: Props{Top: prop}}
1475 | css := fmt.Sprintf(".test{top:%s;}", expected)
1476 | runTest(t, st, css)
1477 | }
1478 | }
1479 |
1480 | func TestStyle_VerticalAlign(t *testing.T) {
1481 | testCases := map[props.VerticalAlign]string{
1482 | props.VerticalAlignBaseline: "baseline",
1483 | props.VerticalAlignSub: "sub",
1484 | props.VerticalAlignSuper: "super",
1485 | props.VerticalAlignTextTop: "text-top",
1486 | props.VerticalAlignTextBottom: "text-bottom",
1487 | props.VerticalAlignMiddle: "middle",
1488 | props.VerticalAlignTop: "top",
1489 | props.VerticalAlignBottom: "bottom",
1490 | props.VerticalAlign("initial"): "initial",
1491 | }
1492 |
1493 | for prop, expected := range testCases {
1494 | st := &Style{Selector: ".test", Props: Props{VerticalAlign: prop}}
1495 | css := fmt.Sprintf(".test{vertical-align:%s;}", expected)
1496 | runTest(t, st, css)
1497 | }
1498 | }
1499 |
1500 | func TestStyle_WhiteSpace(t *testing.T) {
1501 | testCases := map[props.WhiteSpace]string{
1502 | props.WhiteSpaceNormal: "normal",
1503 | props.WhiteSpaceNowrap: "nowrap",
1504 | props.WhiteSpacePre: "pre",
1505 | props.WhiteSpacePreLine: "pre-line",
1506 | props.WhiteSpacePreWrap: "pre-wrap",
1507 | props.WhiteSpace("initial"): "initial",
1508 | }
1509 |
1510 | for prop, expected := range testCases {
1511 | st := &Style{Selector: ".test", Props: Props{WhiteSpace: prop}}
1512 | css := fmt.Sprintf(".test{white-space:%s;}", expected)
1513 | runTest(t, st, css)
1514 | }
1515 | }
1516 |
1517 | func TestStyle_Width(t *testing.T) {
1518 | testCases := map[props.Unit]string{
1519 | props.UnitPx(20): "20px",
1520 | props.UnitRem(2): "2.000rem",
1521 | props.UnitPercent(50): "50.00%",
1522 | props.UnitAuto(): "auto",
1523 | props.UnitRaw(0): "0",
1524 | }
1525 |
1526 | for prop, expected := range testCases {
1527 | st := &Style{Selector: ".test", Props: Props{Width: prop}}
1528 | css := fmt.Sprintf(".test{width:%s;}", expected)
1529 | runTest(t, st, css)
1530 | }
1531 | }
1532 |
1533 | func TestStyle_Visibility(t *testing.T) {
1534 | testCases := map[props.Visibility]string{
1535 | props.VisibilityVisible: "visible",
1536 | props.VisibilityHidden: "hidden",
1537 | props.VisibilityCollapse: "collapse",
1538 | props.Visibility("initial"): "initial",
1539 | }
1540 |
1541 | for prop, expected := range testCases {
1542 | st := &Style{Selector: ".test", Props: Props{Visibility: prop}}
1543 | css := fmt.Sprintf(".test{visibility:%s;}", expected)
1544 | runTest(t, st, css)
1545 | }
1546 | }
1547 |
1548 | func TestStyle_ZIndex(t *testing.T) {
1549 | testCases := map[props.Unit]string{
1550 | props.UnitRaw(1): "1",
1551 | props.UnitRaw(10): "10",
1552 | props.UnitAuto(): "auto",
1553 | }
1554 |
1555 | for prop, expected := range testCases {
1556 | st := &Style{Selector: ".test", Props: Props{ZIndex: prop}}
1557 | css := fmt.Sprintf(".test{z-index:%s;}", expected)
1558 | runTest(t, st, css)
1559 | }
1560 | }
1561 |
--------------------------------------------------------------------------------
/variables/colors.go:
--------------------------------------------------------------------------------
1 | package variables
2 |
3 | import "github.com/AccentDesign/gcss/props"
4 |
5 | var (
6 | Black = props.ColorRGBA(0, 0, 0, 255)
7 | White = props.ColorRGBA(255, 255, 255, 255)
8 | Slate50 = props.ColorRGBA(248, 250, 252, 255)
9 | Slate100 = props.ColorRGBA(241, 245, 249, 255)
10 | Slate200 = props.ColorRGBA(226, 232, 240, 255)
11 | Slate300 = props.ColorRGBA(203, 213, 225, 255)
12 | Slate400 = props.ColorRGBA(148, 163, 184, 255)
13 | Slate500 = props.ColorRGBA(100, 116, 139, 255)
14 | Slate600 = props.ColorRGBA(71, 85, 105, 255)
15 | Slate700 = props.ColorRGBA(51, 65, 85, 255)
16 | Slate800 = props.ColorRGBA(30, 41, 59, 255)
17 | Slate900 = props.ColorRGBA(15, 23, 42, 255)
18 | Slate950 = props.ColorRGBA(2, 6, 23, 255)
19 | Gray50 = props.ColorRGBA(249, 250, 251, 255)
20 | Gray100 = props.ColorRGBA(243, 244, 246, 255)
21 | Gray200 = props.ColorRGBA(229, 231, 235, 255)
22 | Gray300 = props.ColorRGBA(209, 213, 219, 255)
23 | Gray400 = props.ColorRGBA(156, 163, 175, 255)
24 | Gray500 = props.ColorRGBA(107, 114, 128, 255)
25 | Gray600 = props.ColorRGBA(75, 85, 99, 255)
26 | Gray700 = props.ColorRGBA(55, 65, 81, 255)
27 | Gray800 = props.ColorRGBA(31, 41, 55, 255)
28 | Gray900 = props.ColorRGBA(17, 24, 39, 255)
29 | Gray950 = props.ColorRGBA(3, 7, 18, 255)
30 | Zinc50 = props.ColorRGBA(250, 250, 250, 255)
31 | Zinc100 = props.ColorRGBA(244, 244, 245, 255)
32 | Zinc200 = props.ColorRGBA(228, 228, 231, 255)
33 | Zinc300 = props.ColorRGBA(212, 212, 216, 255)
34 | Zinc400 = props.ColorRGBA(161, 161, 170, 255)
35 | Zinc500 = props.ColorRGBA(113, 113, 122, 255)
36 | Zinc600 = props.ColorRGBA(82, 82, 91, 255)
37 | Zinc700 = props.ColorRGBA(63, 63, 70, 255)
38 | Zinc800 = props.ColorRGBA(39, 39, 42, 255)
39 | Zinc900 = props.ColorRGBA(24, 24, 27, 255)
40 | Zinc950 = props.ColorRGBA(9, 9, 11, 255)
41 | Neutral50 = props.ColorRGBA(250, 250, 250, 255)
42 | Neutral100 = props.ColorRGBA(245, 245, 245, 255)
43 | Neutral200 = props.ColorRGBA(229, 229, 229, 255)
44 | Neutral300 = props.ColorRGBA(212, 212, 212, 255)
45 | Neutral400 = props.ColorRGBA(163, 163, 163, 255)
46 | Neutral500 = props.ColorRGBA(115, 115, 115, 255)
47 | Neutral600 = props.ColorRGBA(82, 82, 82, 255)
48 | Neutral700 = props.ColorRGBA(64, 64, 64, 255)
49 | Neutral800 = props.ColorRGBA(38, 38, 38, 255)
50 | Neutral900 = props.ColorRGBA(23, 23, 23, 255)
51 | Neutral950 = props.ColorRGBA(10, 10, 10, 255)
52 | Stone50 = props.ColorRGBA(250, 250, 249, 255)
53 | Stone100 = props.ColorRGBA(245, 245, 244, 255)
54 | Stone200 = props.ColorRGBA(231, 229, 228, 255)
55 | Stone300 = props.ColorRGBA(214, 211, 209, 255)
56 | Stone400 = props.ColorRGBA(168, 162, 158, 255)
57 | Stone500 = props.ColorRGBA(120, 113, 108, 255)
58 | Stone600 = props.ColorRGBA(87, 83, 78, 255)
59 | Stone700 = props.ColorRGBA(68, 64, 60, 255)
60 | Stone800 = props.ColorRGBA(41, 37, 36, 255)
61 | Stone900 = props.ColorRGBA(28, 25, 23, 255)
62 | Stone950 = props.ColorRGBA(12, 10, 9, 255)
63 | Red50 = props.ColorRGBA(254, 242, 242, 255)
64 | Red100 = props.ColorRGBA(254, 226, 226, 255)
65 | Red200 = props.ColorRGBA(254, 202, 202, 255)
66 | Red300 = props.ColorRGBA(252, 165, 165, 255)
67 | Red400 = props.ColorRGBA(248, 113, 113, 255)
68 | Red500 = props.ColorRGBA(239, 68, 68, 255)
69 | Red600 = props.ColorRGBA(220, 38, 38, 255)
70 | Red700 = props.ColorRGBA(185, 28, 28, 255)
71 | Red800 = props.ColorRGBA(153, 27, 27, 255)
72 | Red900 = props.ColorRGBA(127, 29, 29, 255)
73 | Red950 = props.ColorRGBA(69, 10, 10, 255)
74 | Orange50 = props.ColorRGBA(255, 247, 237, 255)
75 | Orange100 = props.ColorRGBA(255, 237, 213, 255)
76 | Orange200 = props.ColorRGBA(254, 215, 170, 255)
77 | Orange300 = props.ColorRGBA(253, 186, 116, 255)
78 | Orange400 = props.ColorRGBA(251, 146, 60, 255)
79 | Orange500 = props.ColorRGBA(249, 115, 22, 255)
80 | Orange600 = props.ColorRGBA(234, 88, 12, 255)
81 | Orange700 = props.ColorRGBA(194, 65, 12, 255)
82 | Orange800 = props.ColorRGBA(154, 52, 18, 255)
83 | Orange900 = props.ColorRGBA(124, 45, 18, 255)
84 | Orange950 = props.ColorRGBA(67, 20, 7, 255)
85 | Amber50 = props.ColorRGBA(255, 251, 235, 255)
86 | Amber100 = props.ColorRGBA(254, 243, 199, 255)
87 | Amber200 = props.ColorRGBA(253, 230, 138, 255)
88 | Amber300 = props.ColorRGBA(252, 211, 77, 255)
89 | Amber400 = props.ColorRGBA(251, 191, 36, 255)
90 | Amber500 = props.ColorRGBA(245, 158, 11, 255)
91 | Amber600 = props.ColorRGBA(217, 119, 6, 255)
92 | Amber700 = props.ColorRGBA(180, 83, 9, 255)
93 | Amber800 = props.ColorRGBA(146, 64, 14, 255)
94 | Amber900 = props.ColorRGBA(120, 53, 15, 255)
95 | Amber950 = props.ColorRGBA(69, 26, 3, 255)
96 | Yellow50 = props.ColorRGBA(254, 252, 232, 255)
97 | Yellow100 = props.ColorRGBA(254, 249, 195, 255)
98 | Yellow200 = props.ColorRGBA(254, 240, 138, 255)
99 | Yellow300 = props.ColorRGBA(253, 224, 71, 255)
100 | Yellow400 = props.ColorRGBA(250, 204, 21, 255)
101 | Yellow500 = props.ColorRGBA(234, 179, 8, 255)
102 | Yellow600 = props.ColorRGBA(202, 138, 4, 255)
103 | Yellow700 = props.ColorRGBA(161, 98, 7, 255)
104 | Yellow800 = props.ColorRGBA(133, 77, 14, 255)
105 | Yellow900 = props.ColorRGBA(113, 63, 18, 255)
106 | Yellow950 = props.ColorRGBA(66, 32, 6, 255)
107 | Lime50 = props.ColorRGBA(247, 254, 231, 255)
108 | Lime100 = props.ColorRGBA(236, 252, 203, 255)
109 | Lime200 = props.ColorRGBA(217, 249, 157, 255)
110 | Lime300 = props.ColorRGBA(190, 242, 100, 255)
111 | Lime400 = props.ColorRGBA(163, 230, 53, 255)
112 | Lime500 = props.ColorRGBA(132, 204, 22, 255)
113 | Lime600 = props.ColorRGBA(101, 163, 13, 255)
114 | Lime700 = props.ColorRGBA(77, 124, 15, 255)
115 | Lime800 = props.ColorRGBA(63, 98, 18, 255)
116 | Lime900 = props.ColorRGBA(54, 83, 20, 255)
117 | Lime950 = props.ColorRGBA(26, 46, 5, 255)
118 | Green50 = props.ColorRGBA(240, 253, 244, 255)
119 | Green100 = props.ColorRGBA(220, 252, 231, 255)
120 | Green200 = props.ColorRGBA(187, 247, 208, 255)
121 | Green300 = props.ColorRGBA(134, 239, 172, 255)
122 | Green400 = props.ColorRGBA(74, 222, 128, 255)
123 | Green500 = props.ColorRGBA(34, 197, 94, 255)
124 | Green600 = props.ColorRGBA(22, 163, 74, 255)
125 | Green700 = props.ColorRGBA(21, 128, 61, 255)
126 | Green800 = props.ColorRGBA(22, 101, 52, 255)
127 | Green900 = props.ColorRGBA(20, 83, 45, 255)
128 | Green950 = props.ColorRGBA(5, 46, 22, 255)
129 | Emerald50 = props.ColorRGBA(236, 253, 245, 255)
130 | Emerald100 = props.ColorRGBA(209, 250, 229, 255)
131 | Emerald200 = props.ColorRGBA(167, 243, 208, 255)
132 | Emerald300 = props.ColorRGBA(110, 231, 183, 255)
133 | Emerald400 = props.ColorRGBA(52, 211, 153, 255)
134 | Emerald500 = props.ColorRGBA(16, 185, 129, 255)
135 | Emerald600 = props.ColorRGBA(5, 150, 105, 255)
136 | Emerald700 = props.ColorRGBA(4, 120, 87, 255)
137 | Emerald800 = props.ColorRGBA(6, 95, 70, 255)
138 | Emerald900 = props.ColorRGBA(6, 78, 59, 255)
139 | Emerald950 = props.ColorRGBA(2, 44, 34, 255)
140 | Teal50 = props.ColorRGBA(240, 253, 250, 255)
141 | Teal100 = props.ColorRGBA(204, 251, 241, 255)
142 | Teal200 = props.ColorRGBA(153, 246, 228, 255)
143 | Teal300 = props.ColorRGBA(94, 234, 212, 255)
144 | Teal400 = props.ColorRGBA(45, 212, 191, 255)
145 | Teal500 = props.ColorRGBA(20, 184, 166, 255)
146 | Teal600 = props.ColorRGBA(13, 148, 136, 255)
147 | Teal700 = props.ColorRGBA(15, 118, 110, 255)
148 | Teal800 = props.ColorRGBA(17, 94, 89, 255)
149 | Teal900 = props.ColorRGBA(19, 78, 74, 255)
150 | Teal950 = props.ColorRGBA(4, 47, 46, 255)
151 | Cyan50 = props.ColorRGBA(236, 254, 255, 255)
152 | Cyan100 = props.ColorRGBA(207, 250, 254, 255)
153 | Cyan200 = props.ColorRGBA(165, 243, 252, 255)
154 | Cyan300 = props.ColorRGBA(103, 232, 249, 255)
155 | Cyan400 = props.ColorRGBA(34, 211, 238, 255)
156 | Cyan500 = props.ColorRGBA(6, 182, 212, 255)
157 | Cyan600 = props.ColorRGBA(8, 145, 178, 255)
158 | Cyan700 = props.ColorRGBA(14, 116, 144, 255)
159 | Cyan800 = props.ColorRGBA(21, 94, 117, 255)
160 | Cyan900 = props.ColorRGBA(22, 78, 99, 255)
161 | Cyan950 = props.ColorRGBA(8, 51, 68, 255)
162 | Sky50 = props.ColorRGBA(240, 249, 255, 255)
163 | Sky100 = props.ColorRGBA(224, 242, 254, 255)
164 | Sky200 = props.ColorRGBA(186, 230, 253, 255)
165 | Sky300 = props.ColorRGBA(125, 211, 252, 255)
166 | Sky400 = props.ColorRGBA(56, 189, 248, 255)
167 | Sky500 = props.ColorRGBA(14, 165, 233, 255)
168 | Sky600 = props.ColorRGBA(2, 132, 199, 255)
169 | Sky700 = props.ColorRGBA(3, 105, 161, 255)
170 | Sky800 = props.ColorRGBA(7, 89, 133, 255)
171 | Sky900 = props.ColorRGBA(12, 74, 110, 255)
172 | Sky950 = props.ColorRGBA(8, 47, 73, 255)
173 | Blue50 = props.ColorRGBA(239, 246, 255, 255)
174 | Blue100 = props.ColorRGBA(219, 234, 254, 255)
175 | Blue200 = props.ColorRGBA(191, 219, 254, 255)
176 | Blue300 = props.ColorRGBA(147, 197, 253, 255)
177 | Blue400 = props.ColorRGBA(96, 165, 250, 255)
178 | Blue500 = props.ColorRGBA(59, 130, 246, 255)
179 | Blue600 = props.ColorRGBA(37, 99, 235, 255)
180 | Blue700 = props.ColorRGBA(29, 78, 216, 255)
181 | Blue800 = props.ColorRGBA(30, 64, 175, 255)
182 | Blue900 = props.ColorRGBA(30, 58, 138, 255)
183 | Blue950 = props.ColorRGBA(23, 37, 84, 255)
184 | Indigo50 = props.ColorRGBA(238, 242, 255, 255)
185 | Indigo100 = props.ColorRGBA(224, 231, 255, 255)
186 | Indigo200 = props.ColorRGBA(199, 210, 254, 255)
187 | Indigo300 = props.ColorRGBA(165, 180, 252, 255)
188 | Indigo400 = props.ColorRGBA(129, 140, 248, 255)
189 | Indigo500 = props.ColorRGBA(99, 102, 241, 255)
190 | Indigo600 = props.ColorRGBA(79, 70, 229, 255)
191 | Indigo700 = props.ColorRGBA(67, 56, 202, 255)
192 | Indigo800 = props.ColorRGBA(55, 48, 163, 255)
193 | Indigo900 = props.ColorRGBA(49, 46, 129, 255)
194 | Indigo950 = props.ColorRGBA(30, 27, 75, 255)
195 | Violet50 = props.ColorRGBA(245, 243, 255, 255)
196 | Violet100 = props.ColorRGBA(237, 233, 254, 255)
197 | Violet200 = props.ColorRGBA(221, 214, 254, 255)
198 | Violet300 = props.ColorRGBA(196, 181, 253, 255)
199 | Violet400 = props.ColorRGBA(167, 139, 250, 255)
200 | Violet500 = props.ColorRGBA(139, 92, 246, 255)
201 | Violet600 = props.ColorRGBA(124, 58, 237, 255)
202 | Violet700 = props.ColorRGBA(109, 40, 217, 255)
203 | Violet800 = props.ColorRGBA(91, 33, 182, 255)
204 | Violet900 = props.ColorRGBA(76, 29, 149, 255)
205 | Violet950 = props.ColorRGBA(46, 16, 101, 255)
206 | Purple50 = props.ColorRGBA(250, 245, 255, 255)
207 | Purple100 = props.ColorRGBA(243, 232, 255, 255)
208 | Purple200 = props.ColorRGBA(233, 213, 255, 255)
209 | Purple300 = props.ColorRGBA(216, 180, 254, 255)
210 | Purple400 = props.ColorRGBA(192, 132, 252, 255)
211 | Purple500 = props.ColorRGBA(168, 85, 247, 255)
212 | Purple600 = props.ColorRGBA(147, 51, 234, 255)
213 | Purple700 = props.ColorRGBA(126, 34, 206, 255)
214 | Purple800 = props.ColorRGBA(107, 33, 168, 255)
215 | Purple900 = props.ColorRGBA(88, 28, 135, 255)
216 | Purple950 = props.ColorRGBA(59, 7, 100, 255)
217 | Fuchsia50 = props.ColorRGBA(253, 244, 255, 255)
218 | Fuchsia100 = props.ColorRGBA(250, 232, 255, 255)
219 | Fuchsia200 = props.ColorRGBA(245, 208, 254, 255)
220 | Fuchsia300 = props.ColorRGBA(240, 171, 252, 255)
221 | Fuchsia400 = props.ColorRGBA(232, 121, 249, 255)
222 | Fuchsia500 = props.ColorRGBA(217, 70, 239, 255)
223 | Fuchsia600 = props.ColorRGBA(192, 38, 211, 255)
224 | Fuchsia700 = props.ColorRGBA(162, 28, 175, 255)
225 | Fuchsia800 = props.ColorRGBA(134, 25, 143, 255)
226 | Fuchsia900 = props.ColorRGBA(112, 26, 117, 255)
227 | Fuchsia950 = props.ColorRGBA(74, 4, 78, 255)
228 | Pink50 = props.ColorRGBA(253, 242, 248, 255)
229 | Pink100 = props.ColorRGBA(252, 231, 243, 255)
230 | Pink200 = props.ColorRGBA(251, 207, 232, 255)
231 | Pink300 = props.ColorRGBA(249, 168, 212, 255)
232 | Pink400 = props.ColorRGBA(244, 114, 182, 255)
233 | Pink500 = props.ColorRGBA(236, 72, 153, 255)
234 | Pink600 = props.ColorRGBA(219, 39, 119, 255)
235 | Pink700 = props.ColorRGBA(190, 24, 93, 255)
236 | Pink800 = props.ColorRGBA(157, 23, 77, 255)
237 | Pink900 = props.ColorRGBA(131, 24, 67, 255)
238 | Pink950 = props.ColorRGBA(80, 7, 36, 255)
239 | Rose50 = props.ColorRGBA(255, 241, 242, 255)
240 | Rose100 = props.ColorRGBA(255, 228, 230, 255)
241 | Rose200 = props.ColorRGBA(254, 205, 211, 255)
242 | Rose300 = props.ColorRGBA(253, 164, 175, 255)
243 | Rose400 = props.ColorRGBA(251, 113, 133, 255)
244 | Rose500 = props.ColorRGBA(244, 63, 94, 255)
245 | Rose600 = props.ColorRGBA(225, 29, 72, 255)
246 | Rose700 = props.ColorRGBA(190, 18, 60, 255)
247 | Rose800 = props.ColorRGBA(159, 18, 57, 255)
248 | Rose900 = props.ColorRGBA(136, 19, 55, 255)
249 | Rose950 = props.ColorRGBA(76, 5, 25, 255)
250 | )
251 |
--------------------------------------------------------------------------------
/variables/sizes.go:
--------------------------------------------------------------------------------
1 | package variables
2 |
3 | import "github.com/AccentDesign/gcss/props"
4 |
5 | var (
6 | Size0 = props.UnitRaw(0)
7 | Size0H = props.UnitRem(0.125)
8 | Size1 = props.UnitRem(0.25)
9 | Size1H = props.UnitRem(0.375)
10 | Size2 = props.UnitRem(0.5)
11 | Size2H = props.UnitRem(0.625)
12 | Size3 = props.UnitRem(0.75)
13 | Size3H = props.UnitRem(0.875)
14 | Size4 = props.UnitRem(1)
15 | Size5 = props.UnitRem(1.25)
16 | Size6 = props.UnitRem(1.5)
17 | Size7 = props.UnitRem(1.75)
18 | Size8 = props.UnitRem(2)
19 | Size9 = props.UnitRem(2.25)
20 | Size10 = props.UnitRem(2.5)
21 | Size11 = props.UnitRem(2.75)
22 | Size12 = props.UnitRem(3)
23 | Size14 = props.UnitRem(3.5)
24 | Size16 = props.UnitRem(4)
25 | Size20 = props.UnitRem(5)
26 | Size24 = props.UnitRem(6)
27 | Size28 = props.UnitRem(7)
28 | Size32 = props.UnitRem(8)
29 | Size36 = props.UnitRem(9)
30 | Size40 = props.UnitRem(10)
31 | Size44 = props.UnitRem(11)
32 | Size48 = props.UnitRem(12)
33 | Size52 = props.UnitRem(13)
34 | Size56 = props.UnitRem(14)
35 | Size60 = props.UnitRem(15)
36 | Size64 = props.UnitRem(16)
37 | Size72 = props.UnitRem(18)
38 | Size80 = props.UnitRem(20)
39 | Size96 = props.UnitRem(24)
40 | Full = props.UnitPercent(100)
41 | FullRounded = props.UnitPx(9999)
42 | FullScreenHeight = props.UnitVh(100)
43 | FullScreenWidth = props.UnitVw(100)
44 | )
45 |
--------------------------------------------------------------------------------