├── .editorconfig ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ ├── Css_test.re └── Selectors_test.re ├── bsconfig.json ├── docs ├── api │ ├── Css.html │ └── Css_Colors.html ├── block-script.js ├── elasticlunr.js ├── fonts │ ├── FiraCode-Bold.woff2 │ ├── FiraCode-Light.woff2 │ ├── FiraCode-Medium.woff2 │ ├── FiraCode-Regular.woff2 │ └── LICENSE.md ├── index.html ├── script.js ├── search.html ├── search.js ├── searchables.json ├── searchables.json.index.js └── styles.css ├── package.json ├── src ├── Css.re ├── Css.rei ├── Css_Colors.re ├── Css_Types.re └── Css_Types.rei └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .DS_Store 3 | .idea 4 | *.iml 5 | /node_modules* 6 | /lib/bs 7 | /lib/ocaml 8 | /lib/js/__tests__ 9 | npm-debug.log 10 | *.js 11 | .merlin 12 | .bsb.lock 13 | *.log 14 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .merlin 2 | /.idea 3 | /.vscode 4 | .editorconfig 5 | *.tgz 6 | *.iml 7 | webpack.config.js 8 | /example 9 | /lib/bs 10 | /lib/js/__tests__ 11 | .bsb.lock 12 | .*.swp 13 | docs/ 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.tabSize": 2, 4 | "editor.formatOnSave": true, 5 | "files.trimTrailingWhitespace": true, 6 | "files.insertFinalNewline": true 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [10.0.1] - 2019-09-23 4 | 5 | ### Added 6 | 7 | - textDecorationLine 8 | 9 | ### Changed 10 | 11 | - Pseudo classes selectors have been updated and documented 12 | - Update properties : backgroundAttachment, backgroundClip, backgroundOrigin, backgroundRepeat, 13 | textOverflow, textDecorationLine, textDecorationStyle, width, wordWrap 14 | 15 | ## [10.0.0] - 2019-09-04 16 | 17 | Major release because of the breaking change in shadows definition. 18 | 19 | ### Breaking change 20 | 21 | - Css.rule is now an abstract type [#153](https://github.com/SentiaAnalytics/bs-css/issues/153) 22 | - Update shadow definition [#148](https://github.com/SentiaAnalytics/bs-css/issues/148) 23 | 24 | boxShadow has been changed to be a rule to allow for `none`, `important` and all other rule related functions. 25 | 26 | It means that the shadow properties must be updated to the following patterns: 27 | ```reason 28 | // before: 29 | boxShadow(~x=px(1), ~y=px(2)); 30 | boxShadows([boxShadow(yellow), boxShadow(red)]); 31 | textShadow(~y=px(3), ~blur=px(2), black); 32 | textShadows([textShadow(~y=px(3), ~blur=px(2), black), textShadow(~x=px(3), green)]); 33 | 34 | // after: 35 | boxShadow(Shadow.box(~x=px(1), ~y=px(2))); 36 | boxShadows([Shadow.box(yellow), Shadow.box(red)]); 37 | textShadow(Shadow.text(~y=px(3), ~blur=px(2), black)); 38 | textShadows([Shadow.text(~y=px(3), ~blur=px(2), black), Shadow.text(~x=px(3), green)]); 39 | ``` 40 | 41 | ### Added 42 | 43 | - Support for for `object-fit` property by [@kuy](https://github.com/kuy) - [#125](https://github.com/SentiaAnalytics/bs-css/pull/125) 44 | - Add `fit-content` option for width property by [@mwarni](https://github.com/mwarni) - [#149](https://github.com/SentiaAnalytics/bs-css/pull/149) 45 | - Add support for `grid-template-areas` and `grid-area` by [@drew887](https://github.com/drew887) - [#156](https://github.com/SentiaAnalytics/bs-css/issues/156) 46 | 47 | ### Fixed 48 | 49 | - BoxShadow: none doesn't work - [#148](https://github.com/SentiaAnalytics/bs-css/issues/148) 50 | - !important doesn't apply to boxShadow - [#147](https://github.com/SentiaAnalytics/bs-css/issues/147) 51 | 52 | ### Changed 53 | 54 | - Move types to Css.Types, updated some css properties 55 | - Use yarn instead of npm 56 | 57 | ## 9.0.1 - 2019-07-01 58 | 59 | This is a major release: bs-css now depend on emotion 10.x instead of 9.x (see [#114](https://github.com/SentiaAnalytics/bs-css/pull/114)). 60 | 61 | - :bug: [#139](https://github.com/SentiaAnalytics/bs-css/issues/139) - Units for hsl(a) 62 | - :bug: [#138](https://github.com/SentiaAnalytics/bs-css/issues/138) - (top, bottom, left, right) properties should also belong to the 'cascading' type 63 | - :bug: [#133](https://github.com/SentiaAnalytics/bs-css/issues/133) - Support pixel units for gradient color stops 64 | - :rocket: [#131](https://github.com/SentiaAnalytics/bs-css/issues/131) - Support for "direction" property 65 | - :bug: [#109](https://github.com/SentiaAnalytics/bs-css/issues/109) - flexShrink and flex should accept a float instead of a int 66 | 67 | contributors: [@simonkberg](https://github.com/simonkberg), [@JakubMarkiewicz](https://github.com/JakubMarkiewicz), [@remitbri](https://github.com/remitbri), [@lucasweng](https://github.com/lucasweng) 68 | 69 | ## 8.0.4 - 2019-04-04 70 | 71 | - :rocket: [#125](https://github.com/SentiaAnalytics/bs-css/pull/125) - Add textShadows 72 | - :house: webpack is replaced by parcel, zero config local server 73 | 74 | contributors: [@Freddy03h](https://github.com/Freddy03h) 75 | 76 | ## 8.0.3 - 2019-03-20 77 | 78 | - :rocket: [#118](https://github.com/SentiaAnalytics/bs-css/issues/118) - Minmax should allow fr 79 | - :rocket: Add a toJson function to convert a list of rules to a json object 80 | 81 | contributors: [@JakubMarkiewicz](https://github.com/JakubMarkiewicz) 82 | 83 | ## 8.0.2 - 2019-02-25 84 | 85 | - :rocket: [#119](https://github.com/SentiaAnalytics/bs-css/issues/119) - Missing resize attribute 86 | - :rocket: [#117](https://github.com/SentiaAnalytics/bs-css/issues/117) - Add `spaceEvenly` for justifyContent and alignContent 87 | - :rocket: [#113](https://github.com/SentiaAnalytics/bs-css/pull/113) - Minmax in grid implementation 88 | 89 | contributors: [@JakubMarkiewicz](https://github.com/JakubMarkiewicz), [@lucasweng](https://github.com/lucasweng), [@wegry](https://github.com/wegry) 90 | 91 | ## 8.0.1 - 2019-01-31 92 | 93 | - :bug: [#108](https://github.com/SentiaAnalytics/bs-css/issues/108) - Wrong value for minWidth/maxWidth 94 | - :rocket: [#111](https://github.com/SentiaAnalytics/bs-css/pull/111) - Fill all allowed display values 95 | 96 | contributors: [@sean-clayton](https://github.com/sean-clayton), [@c19](https://github.com/c19) 97 | 98 | ## 8.0.0 - 2018-12-18 99 | 100 | - :rocket: [#76](https://github.com/SentiaAnalytics/bs-css/issues/76) - Add support for commonly used name for font weight 101 | - :bug: [#86](https://github.com/SentiaAnalytics/bs-css/issues/86) - Losing styles when merging nested selectors 102 | - :bug: [#105](https://github.com/SentiaAnalytics/bs-css/issues/105) - flexGrow should accept a float instead of a int 103 | - :house: [#95](https://github.com/SentiaAnalytics/bs-css/issues/95) - Remove unnecessary reverse of styles before passing to JS obj 104 | - :house: [#91](https://github.com/SentiaAnalytics/bs-css/issues/91) - Swapped glamor runtime to emotion runtime 105 | 106 | #### Breaking changes 107 | 108 | This version of bs-css is using a new runtime library that replaces glamor. 109 | 110 | This decision is driven by the following points: 111 | 112 | - the author of glamor is no more maintaining its project, the last commit happened more than one year ago, 113 | - emotion is the recommended alternative because it mostly matches glamor's API 114 | - better grid support : glamor uses a lot of the old -ms-grid and -webkit-grid prefixes (with IE11 support) 115 | - Merging styles with sub selectors work as expected and fix a bug bs-css had 116 | 117 | Given that it is a major version, we also changed some functions in the API, especially regarding merging. 118 | 119 | #### Migration 120 | 121 | - `fontWeight` number must now use the `num` constructor: you must change `fontWeight(300)` to `fontWeight(num(300))`. 122 | The benefit is that you can transform your absolute numbers to font names, see https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#Common_weight_name_mapping. 123 | - `merge` was really a `concat` function and could lead to problems as seen in #86. This is why it has been changed to use 124 | the internal library (emotion) and its signature is now : `list(string) => string`. If you want to keep the same functionality 125 | than before, you can use `List.concat` instead or `@` operator. 126 | - if you are mixin reason/js, you need to change your dependency and replace `glamor` with `emotion` 127 | - `flexGrow` accepts a float instead of an int, add a `.` to your int value (for ex, convert `1` to `1.`) 128 | 129 | #### Contributors 130 | 131 | Big thanks to [@baldurh](https://github.com/baldurh) and [@wegry](https://github.com/wegry) for their work on emotion. 132 | 133 | [Unreleased]: https://github.com/SentiaAnalytics/bs-css/compare/v10.0.1...HEAD 134 | [10.0.1]: https://github.com/SentiaAnalytics/bs-css/compare/v10.0.0...v10.0.1 135 | [10.0.0]: https://github.com/SentiaAnalytics/bs-css/compare/v9.0.1...v10.0.0 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 bs-glamor/bs-css developers 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bs-css-core 2 | 3 | Statically typed DSL for writing CSS in ReasonML. 4 | 5 | This is a fork of [bs-css](https://github.com/SentiaAnalytics/bs-css) that 6 | extracts the Css module, so that it can be used by various CSS-in-JS bindings. 7 | See 8 | [bs-react-fela-examples](https://github.com/astrada/bs-react-fela-examples) 9 | and 10 | [bs-styletron-react-examples](https://github.com/astrada/bs-styletron-react-examples) 11 | for practical examples of usage. 12 | 13 | ## Installation 14 | 15 | ```sh 16 | yarn add @astrada/bs-css-core 17 | ``` 18 | 19 | In your `bsconfig.json`, include `"@astrada/bs-css-core"` in the 20 | `bs-dependencies`. 21 | 22 | ## Usage 23 | 24 | ```reason 25 | open BsCssCore; 26 | 27 | type theme = { 28 | textColor: Css.color, 29 | basePadding: Css.length 30 | }; 31 | 32 | let makeStyle = (theme) => 33 | Css.( 34 | style([ 35 | backgroundColor(white), 36 | boxShadows([boxShadow(~y=px(3), ~blur=px(5), rgba(0, 0, 0, 0.3))]), 37 | padding(theme.basePadding), 38 | fontSize(rem(1.5)), 39 | color(theme.textColor), 40 | marginBottom(px(10)) 41 | ]) 42 | ); 43 | ``` 44 | 45 | **Keyframes** 46 | 47 | Define animation keyframes; 48 | 49 | ```reason 50 | open BsCssCore; 51 | 52 | let bounce = 53 | Css.( 54 | keyframes([ 55 | (0, [transform(scale(0.1, 0.1)), opacity(0.0)]), 56 | (60, [transform(scale(1.2, 1.2)), opacity(1.0)]), 57 | (100, [transform(scale(1.0, 1.0)), opacity(1.0)]) 58 | ]) 59 | ); 60 | 61 | let makeStyle = (_theme) => 62 | Css.( 63 | style([ 64 | animationName(bounce), 65 | animationDuration(2000), 66 | width(px(50)), 67 | height(px(50)), 68 | backgroundColor(rgb(255, 0, 0)) 69 | ]) 70 | ); 71 | ``` 72 | 73 | ## Development 74 | 75 | ```sh 76 | yarn start 77 | ``` 78 | 79 | ## Where is the documentation? 80 | 81 | Documentation generated with [redoc](https://github.com/jaredly/redoc) is 82 | published [here](https://astrada.github.io/bs-css-core/). 83 | 84 | ## Thanks 85 | 86 | Thanks to [bs-css](https://github.com/SentiaAnalytics/bs-css) that introduced 87 | the DSL. Thanks to [bs-glamor](https://github.com/poeschko/bs-glamor) which 88 | bs-css was forked from. Thanks to 89 | [elm-css](https://github.com/rtfeldman/elm-css) for DSL design inspiration. 90 | Thanks to @jaredly for [redoc](https://github.com/jaredly/redoc). 91 | -------------------------------------------------------------------------------- /__tests__/Css_test.re: -------------------------------------------------------------------------------- 1 | open Jest; 2 | open Expect; 3 | open Css; 4 | 5 | let toBeJson = x => Expect.toBe(x->Js.Json.stringifyAny); 6 | external toObject: style => Js.t({..}) = "%identity"; 7 | let r = x => style([x])->toObject; 8 | 9 | describe("Color style", () => 10 | test("test values", () => 11 | expect( 12 | ( 13 | r(color(rgb(1, 2, 3))), 14 | r(color(rgba(4, 5, 6, 0.3))), 15 | r(color(hsl(deg(7.), 8., 9.))), 16 | r(color(hsla(deg(10.), 11., 12., `num(0.5)))), 17 | r(color(hsla(rad(4.7), 11., 12., pct(50.)))), 18 | r(color(transparent)), 19 | r(color(hex("FFF"))), 20 | r(color(currentColor)), 21 | ) 22 | ->Js.Json.stringifyAny, 23 | ) 24 | |> toBeJson(( 25 | {"color": "rgb(1, 2, 3)"}, 26 | {"color": "rgba(4, 5, 6, 0.3)"}, 27 | {"color": "hsl(7deg, 8%, 9%)"}, 28 | {"color": "hsla(10deg, 11%, 12%, 0.5)"}, 29 | {"color": "hsla(4.7rad, 11%, 12%, 50%)"}, 30 | {"color": "transparent"}, 31 | {"color": "#FFF"}, 32 | {"color": "currentColor"}, 33 | )) 34 | ) 35 | ); 36 | 37 | describe("Label", () => { 38 | test("test value", () => 39 | expect(r(label("a"))->Js.Json.stringifyAny) |> toBeJson({"label": "a"}) 40 | ); 41 | }); 42 | 43 | describe("Filter", () => 44 | test("test values", () => 45 | expect( 46 | ( 47 | r(filter([`opacity(10.), `invert(20.)])), 48 | r(filter([`blur(`px(20)), `brightness(20.)])), 49 | r( 50 | filter([ 51 | `contrast(30.), 52 | `dropShadow((`px(5), `px(6), `px(7), `rgb((255, 0, 0)))), 53 | ]), 54 | ), 55 | r(filter([`grayscale(10.), `hueRotate(`deg(180.))])), 56 | r(filter([`saturate(10.), `sepia(100.)])), 57 | r(filter([`none])), 58 | r(filter([`inherit_])), 59 | r(filter([`initial])), 60 | r(filter([`unset])), 61 | r(filter([`url("myurl")])), 62 | ) 63 | ->Js.Json.stringifyAny, 64 | ) 65 | |> toBeJson(( 66 | {"filter": "opacity(10%) invert(20%)"}, 67 | {"filter": "blur(20px) brightness(20%)"}, 68 | {"filter": "contrast(30%) drop-shadow(5px 6px 7px rgb(255, 0, 0))"}, 69 | {"filter": "grayscale(10%) hue-rotate(180deg)"}, 70 | {"filter": "saturate(10%) sepia(100%)"}, 71 | {"filter": "none"}, 72 | {"filter": "inherit"}, 73 | {"filter": "initial"}, 74 | {"filter": "unset"}, 75 | {"filter": "url(myurl)"}, 76 | )) 77 | ) 78 | ); 79 | 80 | describe("Angle", () => 81 | test("test values", () => 82 | expect( 83 | ( 84 | r(transform(rotate(deg(1.)))), 85 | r(transform(rotate(rad(6.28)))), 86 | r(transform(rotate(grad(38.8)))), 87 | r(transform(rotate(turn(0.25)))), 88 | ) 89 | ->Js.Json.stringifyAny, 90 | ) 91 | |> toBeJson(( 92 | {"transform": "rotate(1deg)"}, 93 | {"transform": "rotate(6.28rad)"}, 94 | {"transform": "rotate(38.8grad)"}, 95 | {"transform": "rotate(0.25turn)"}, 96 | )) 97 | ) 98 | ); 99 | 100 | describe("Direction", () => 101 | test("test values", () => 102 | expect( 103 | ( 104 | r(direction(`ltr)), 105 | r(direction(ltr)), 106 | r(direction(rtl)), 107 | r(direction(inherit_)), 108 | r(direction(unset)), 109 | r(direction(initial)), 110 | ) 111 | ->Js.Json.stringifyAny, 112 | ) 113 | |> toBeJson(( 114 | {"direction": "ltr"}, 115 | {"direction": "ltr"}, 116 | {"direction": "rtl"}, 117 | {"direction": "inherit"}, 118 | {"direction": "unset"}, 119 | {"direction": "initial"}, 120 | )) 121 | ) 122 | ); 123 | 124 | describe("Resize", () => 125 | test("test values", () => 126 | expect( 127 | ( 128 | r(resize(none)), 129 | r(resize(both)), 130 | r(resize(horizontal)), 131 | r(resize(vertical)), 132 | r(resize(block)), 133 | r(resize(inline)), 134 | r(resize(inherit_)), 135 | r(resize(unset)), 136 | r(resize(initial)), 137 | ) 138 | ->Js.Json.stringifyAny, 139 | ) 140 | |> toBeJson(( 141 | {"resize": "none"}, 142 | {"resize": "both"}, 143 | {"resize": "horizontal"}, 144 | {"resize": "vertical"}, 145 | {"resize": "block"}, 146 | {"resize": "inline"}, 147 | {"resize": "inherit"}, 148 | {"resize": "unset"}, 149 | {"resize": "initial"}, 150 | )) 151 | ) 152 | ); 153 | 154 | describe("Gradient background", () => 155 | test("test values", () => 156 | expect( 157 | ( 158 | r( 159 | background( 160 | linearGradient(deg(45.), [(zero, red), (pct(100.), blue)]), 161 | ), 162 | ), 163 | r( 164 | background( 165 | repeatingLinearGradient( 166 | deg(45.), 167 | [(zero, red), (px(10), blue)], 168 | ), 169 | ), 170 | ), 171 | r(background(radialGradient([(zero, red), (pct(100.), blue)]))), 172 | r( 173 | background( 174 | repeatingRadialGradient([ 175 | (zero, red), 176 | (Calc.(pct(20.) + px(5)), blue), 177 | ]), 178 | ), 179 | ), 180 | ) 181 | ->Js.Json.stringifyAny, 182 | ) 183 | |> toBeJson(( 184 | {"background": "linear-gradient(45deg, #FF0000 0, #0000FF 100%)"}, 185 | { 186 | "background": "repeating-linear-gradient(45deg, #FF0000 0, #0000FF 10px)", 187 | }, 188 | {"background": "radial-gradient(#FF0000 0, #0000FF 100%)"}, 189 | { 190 | "background": "repeating-radial-gradient(#FF0000 0, #0000FF calc(20% + 5px))", 191 | }, 192 | )) 193 | ) 194 | ); 195 | 196 | describe("Position", () => { 197 | test("should use length", () => 198 | expect( 199 | ( 200 | r(top(px(10))), 201 | r(right(rem(1.))), 202 | r(bottom(pct(20.))), 203 | r(left(vh(4.))), 204 | ) 205 | ->Js.Json.stringifyAny, 206 | ) 207 | |> toBeJson(( 208 | {"top": "10px"}, 209 | {"right": "1rem"}, 210 | {"bottom": "20%"}, 211 | {"left": "4vh"}, 212 | )) 213 | ); 214 | 215 | test("should allow cascading", () => 216 | expect( 217 | ( 218 | r(top(initial)), 219 | r(right(inherit_)), 220 | r(bottom(unset)), 221 | r(left(initial)), 222 | ) 223 | ->Js.Json.stringifyAny, 224 | ) 225 | |> toBeJson(( 226 | {"top": "initial"}, 227 | {"right": "inherit"}, 228 | {"bottom": "unset"}, 229 | {"left": "initial"}, 230 | )) 231 | ); 232 | }); 233 | 234 | describe("object-fit", () => 235 | test("test values", () => 236 | expect( 237 | ( 238 | r(objectFit(`fill)), 239 | r(objectFit(`contain)), 240 | r(objectFit(`cover)), 241 | r(objectFit(`none)), 242 | r(objectFit(`scaleDown)), 243 | r(objectFit(`inherit_)), 244 | r(objectFit(`initial)), 245 | r(objectFit(`unset)), 246 | ) 247 | ->Js.Json.stringifyAny, 248 | ) 249 | |> toBeJson(( 250 | {"objectFit": "fill"}, 251 | {"objectFit": "contain"}, 252 | {"objectFit": "cover"}, 253 | {"objectFit": "none"}, 254 | {"objectFit": "scale-down"}, 255 | {"objectFit": "inherit"}, 256 | {"objectFit": "initial"}, 257 | {"objectFit": "unset"}, 258 | )) 259 | ) 260 | ); 261 | 262 | describe("box-shadow", () => { 263 | test("should allow single or list definition", () => 264 | expect( 265 | ( 266 | r(boxShadow(Shadow.box(green))), 267 | r(boxShadows([Shadow.box(yellow), Shadow.box(red)])), 268 | ) 269 | ->Js.Json.stringifyAny, 270 | ) 271 | |> toBeJson(( 272 | {"boxShadow": "0 0 0 0 #008000"}, 273 | {"boxShadow": "0 0 0 0 #FFFF00, 0 0 0 0 #FF0000"}, 274 | )) 275 | ); 276 | 277 | test("should use options when present", () => 278 | expect( 279 | ( 280 | r(boxShadow(Shadow.box(~x=px(1), ~y=px(2), red))), 281 | r(boxShadow(Shadow.box(~inset=true, red))), 282 | ) 283 | ->Js.Json.stringifyAny, 284 | ) 285 | |> toBeJson(( 286 | {"boxShadow": "1px 2px 0 0 #FF0000"}, 287 | {"boxShadow": "0 0 0 0 #FF0000 inset"}, 288 | )) 289 | ); 290 | 291 | test("should allow special values", () => 292 | expect( 293 | ( 294 | r(boxShadow(none)), 295 | r(boxShadow(inherit_)), 296 | r(boxShadow(initial)), 297 | r(boxShadow(unset)), 298 | r(important(boxShadow(none))), 299 | ) 300 | ->Js.Json.stringifyAny, 301 | ) 302 | |> toBeJson(( 303 | {"boxShadow": "none"}, 304 | {"boxShadow": "inherit"}, 305 | {"boxShadow": "initial"}, 306 | {"boxShadow": "unset"}, 307 | {"boxShadow": "none !important"}, 308 | )) 309 | ); 310 | }); 311 | 312 | describe("text-shadow", () => { 313 | test("should allow single or list definition", () => 314 | expect( 315 | ( 316 | r(textShadow(Shadow.text(green))), 317 | r(textShadows([Shadow.text(yellow), Shadow.text(red)])), 318 | ) 319 | ->Js.Json.stringifyAny, 320 | ) 321 | |> toBeJson(( 322 | {"textShadow": "0 0 0 #008000"}, 323 | {"textShadow": "0 0 0 #FFFF00, 0 0 0 #FF0000"}, 324 | )) 325 | ); 326 | 327 | test("should use options when present", () => 328 | expect( 329 | ( 330 | r(textShadow(Shadow.text(~x=px(1), ~y=px(2), red))), 331 | r(textShadow(Shadow.text(~blur=vh(1.), red))), 332 | ) 333 | ->Js.Json.stringifyAny, 334 | ) 335 | |> toBeJson(( 336 | {"textShadow": "1px 2px 0 #FF0000"}, 337 | {"textShadow": "0 0 1vh #FF0000"}, 338 | )) 339 | ); 340 | 341 | test("should allow special values", () => 342 | expect( 343 | ( 344 | r(textShadow(none)), 345 | r(textShadow(inherit_)), 346 | r(textShadow(initial)), 347 | r(textShadow(unset)), 348 | r(important(textShadow(none))), 349 | ) 350 | ->Js.Json.stringifyAny, 351 | ) 352 | |> toBeJson(( 353 | {"textShadow": "none"}, 354 | {"textShadow": "inherit"}, 355 | {"textShadow": "initial"}, 356 | {"textShadow": "unset"}, 357 | {"textShadow": "none !important"}, 358 | )) 359 | ); 360 | }); 361 | 362 | describe("transitions", () => { 363 | test("should allow single or list definition", () => 364 | expect( 365 | ( 366 | r(transition("transform")), 367 | r( 368 | transitions([ 369 | Transition.shorthand("height"), 370 | Transition.shorthand("top"), 371 | ]), 372 | ), 373 | ) 374 | ->Js.Json.stringifyAny, 375 | ) 376 | |> toBeJson(( 377 | {"transition": "0ms ease 0ms transform"}, 378 | {"transition": "0ms ease 0ms height, 0ms ease 0ms top"}, 379 | )) 380 | ); 381 | 382 | test("should use options when present", () => 383 | expect( 384 | r(transition(~duration=3, ~delay=4, ~timingFunction=easeOut, "top")) 385 | ->Js.Json.stringifyAny, 386 | ) 387 | |> toBeJson({"transition": "3ms ease-out 4ms top"}) 388 | ); 389 | }); 390 | 391 | external toAN: rule => animationName = "%identity"; 392 | let toAnimationName: string => animationName = x => selector(x, []) |> toAN; 393 | 394 | describe("animation", () => { 395 | test("should allow single or list definition", () => 396 | expect( 397 | ( 398 | r(animation(toAnimationName("a"))), 399 | r( 400 | animations([ 401 | Animation.shorthand(toAnimationName("a1")), 402 | Animation.shorthand(toAnimationName("a2")), 403 | ]), 404 | ), 405 | ) 406 | ->Js.Json.stringifyAny, 407 | ) 408 | |> toBeJson(( 409 | {"animation": "a 0ms ease 0ms 1 normal none running"}, 410 | { 411 | "animation": "a1 0ms ease 0ms 1 normal none running, a2 0ms ease 0ms 1 normal none running", 412 | }, 413 | )) 414 | ); 415 | 416 | test("should use options when present", () => 417 | expect( 418 | r( 419 | animation( 420 | ~duration=300, 421 | ~delay=400, 422 | ~direction=reverse, 423 | ~timingFunction=linear, 424 | ~fillMode=forwards, 425 | ~playState=running, 426 | ~iterationCount=infinite, 427 | toAnimationName("a"), 428 | ), 429 | ) 430 | ->Js.Json.stringifyAny, 431 | ) 432 | |> toBeJson({ 433 | "animation": "a 300ms linear 400ms infinite reverse forwards running", 434 | }) 435 | ); 436 | }); 437 | 438 | describe("Word spacing", () => 439 | test("test values", () => 440 | expect( 441 | ( 442 | r(wordSpacing(`normal)), 443 | r(wordSpacing(vh(1.))), 444 | r(wordSpacing(pct(50.))), 445 | r(wordSpacing(inherit_)), 446 | ) 447 | ->Js.Json.stringifyAny, 448 | ) 449 | |> toBeJson(( 450 | {"wordSpacing": "normal"}, 451 | {"wordSpacing": "1vh"}, 452 | {"wordSpacing": "50%"}, 453 | {"wordSpacing": "inherit"}, 454 | )) 455 | ) 456 | ); 457 | 458 | describe("gridTemplateAreas", () => { 459 | test("takes acceptable types & cascades", () => 460 | expect( 461 | ( 462 | r(gridTemplateAreas(`none)), 463 | r(gridTemplateAreas(`areas(["a"]))), 464 | r(gridTemplateAreas(`inherit_)), 465 | r(gridTemplateAreas(`initial)), 466 | r(gridTemplateAreas(`unset)), 467 | ) 468 | ->Js.Json.stringifyAny, 469 | ) 470 | |> toBeJson(( 471 | {"gridTemplateAreas": "none"}, 472 | {"gridTemplateAreas": "'a'"}, 473 | {"gridTemplateAreas": "inherit"}, 474 | {"gridTemplateAreas": "initial"}, 475 | {"gridTemplateAreas": "unset"}, 476 | )) 477 | ); 478 | 479 | test("sucessfully combines list", () => 480 | expect( 481 | r(gridTemplateAreas(`areas(["a a a", "b b b"]))) 482 | ->Js.Json.stringifyAny, 483 | ) 484 | |> toBeJson({"gridTemplateAreas": "'a a a' 'b b b'"}) 485 | ); 486 | }); 487 | 488 | describe("GridArea", () => { 489 | test("gridArea takes values & cascades", () => 490 | expect( 491 | ( 492 | r(gridArea(`auto)), 493 | r(gridArea(`ident("a"))), 494 | r(gridArea(`num(1))), 495 | r(gridArea(`numIdent((1, "a")))), 496 | r(gridArea(`span(`num(1)))), 497 | r(gridArea(`span(`ident("a")))), 498 | r(gridArea(`inherit_)), 499 | r(gridArea(`initial)), 500 | r(gridArea(`unset)), 501 | ) 502 | ->Js.Json.stringifyAny, 503 | ) 504 | |> toBeJson(( 505 | {"gridArea": "auto"}, 506 | {"gridArea": "a"}, 507 | {"gridArea": "1"}, 508 | {"gridArea": "1 a"}, 509 | {"gridArea": "span 1"}, 510 | {"gridArea": "span a"}, 511 | {"gridArea": "inherit"}, 512 | {"gridArea": "initial"}, 513 | {"gridArea": "unset"}, 514 | )) 515 | ); 516 | 517 | test("multi-arg functions add in slashes", () => 518 | expect( 519 | ( 520 | r(gridArea2(`auto, `num(1))), 521 | r(gridArea3(`ident("a"), `numIdent((1, "a")), `auto)), 522 | r( 523 | gridArea4(`num(5), `span(`num(16)), `span(`ident("b")), `auto), 524 | ), 525 | ) 526 | ->Js.Json.stringifyAny, 527 | ) 528 | |> toBeJson(( 529 | {"gridArea": "auto / 1"}, 530 | {"gridArea": "a / 1 a / auto"}, 531 | {"gridArea": "5 / span 16 / span b / auto"}, 532 | )) 533 | ); 534 | }); 535 | 536 | describe("backgroundRepeat", () => { 537 | test("test single values", () => 538 | expect( 539 | ( 540 | r(backgroundRepeat(repeatX)), 541 | r(backgroundRepeat(repeatY)), 542 | r(backgroundRepeat(repeat)), 543 | r(backgroundRepeat(space)), 544 | r(backgroundRepeat(round)), 545 | r(backgroundRepeat(noRepeat)), 546 | r(backgroundRepeat(inherit_)), 547 | ) 548 | ->Js.Json.stringifyAny, 549 | ) 550 | |> toBeJson(( 551 | {"backgroundRepeat": "repeat-x"}, 552 | {"backgroundRepeat": "repeat-y"}, 553 | {"backgroundRepeat": "repeat"}, 554 | {"backgroundRepeat": "space"}, 555 | {"backgroundRepeat": "round"}, 556 | {"backgroundRepeat": "no-repeat"}, 557 | {"backgroundRepeat": "inherit"}, 558 | )) 559 | ); 560 | 561 | test("test two values", () => 562 | expect( 563 | ( 564 | r(backgroundRepeat(`hv((repeat, space)))), 565 | r(backgroundRepeat(`hv((repeat, repeat)))), 566 | r(backgroundRepeat(`hv((round, space)))), 567 | r(backgroundRepeat(`hv((noRepeat, round)))), 568 | ) 569 | ->Js.Json.stringifyAny, 570 | ) 571 | |> toBeJson(( 572 | {"backgroundRepeat": "repeat space"}, 573 | {"backgroundRepeat": "repeat repeat"}, 574 | {"backgroundRepeat": "round space"}, 575 | {"backgroundRepeat": "no-repeat round"}, 576 | )) 577 | ); 578 | }); 579 | -------------------------------------------------------------------------------- /__tests__/Selectors_test.re: -------------------------------------------------------------------------------- 1 | open Jest; 2 | open Expect; 3 | open Css; 4 | 5 | let toBeJson = x => Expect.toBe(x->Js.Json.stringifyAny); 6 | external toObject: style => Js.t({..}) = "%identity"; 7 | let r = x => style([x])->toObject; 8 | let ruleSelector = display(block); 9 | let ruleJson = {"display": "block"}; 10 | 11 | describe("Pseudo classes", () => { 12 | test("test selectors that have no parameters", () => 13 | expect( 14 | ( 15 | r(active([ruleSelector])), 16 | r(checked([ruleSelector])), 17 | r(default([ruleSelector])), 18 | r(defined([ruleSelector])), 19 | r(disabled([ruleSelector])), 20 | r(empty([ruleSelector])), 21 | r(enabled([ruleSelector])), 22 | r(first([ruleSelector])), 23 | r(firstChild([ruleSelector])), 24 | r(firstOfType([ruleSelector])), 25 | r(focus([ruleSelector])), 26 | r(focusWithin([ruleSelector])), 27 | r(hover([ruleSelector])), 28 | r(indeterminate([ruleSelector])), 29 | r(inRange([ruleSelector])), 30 | r(invalid([ruleSelector])), 31 | r(lastChild([ruleSelector])), 32 | r(lastOfType([ruleSelector])), 33 | r(link([ruleSelector])), 34 | r(onlyChild([ruleSelector])), 35 | r(onlyOfType([ruleSelector])), 36 | r(optional([ruleSelector])), 37 | r(outOfRange([ruleSelector])), 38 | r(readOnly([ruleSelector])), 39 | r(readWrite([ruleSelector])), 40 | r(required([ruleSelector])), 41 | r(root([ruleSelector])), 42 | r(scope([ruleSelector])), 43 | r(target([ruleSelector])), 44 | r(valid([ruleSelector])), 45 | r(visited([ruleSelector])), 46 | ) 47 | ->Js.Json.stringifyAny, 48 | ) 49 | |> toBeJson(( 50 | {":active": ruleJson}, 51 | {":checked": ruleJson}, 52 | {":default": ruleJson}, 53 | {":defined": ruleJson}, 54 | {":disabled": ruleJson}, 55 | {":empty": ruleJson}, 56 | {":enabled": ruleJson}, 57 | {":first": ruleJson}, 58 | {":first-child": ruleJson}, 59 | {":first-of-type": ruleJson}, 60 | {":focus": ruleJson}, 61 | {":focus-within": ruleJson}, 62 | {":hover": ruleJson}, 63 | {":indeterminate": ruleJson}, 64 | {":in-range": ruleJson}, 65 | {":invalid": ruleJson}, 66 | {":last-child": ruleJson}, 67 | {":last-of-type": ruleJson}, 68 | {":link": ruleJson}, 69 | {":only-child": ruleJson}, 70 | {":only-of-type": ruleJson}, 71 | {":optional": ruleJson}, 72 | {":out-of-range": ruleJson}, 73 | {":read-only": ruleJson}, 74 | {":read-write": ruleJson}, 75 | {":required": ruleJson}, 76 | {":root": ruleJson}, 77 | {":scope": ruleJson}, 78 | {":target": ruleJson}, 79 | {":valid": ruleJson}, 80 | {":visited": ruleJson}, 81 | )) 82 | ); 83 | 84 | test("test host", () => 85 | expect( 86 | ( 87 | r(host([ruleSelector])), 88 | r(host(~selector=".special-custom-element", [ruleSelector])), 89 | ) 90 | ->Js.Json.stringifyAny, 91 | ) 92 | |> toBeJson(( 93 | {":host": ruleJson}, 94 | {":host(.special-custom-element)": ruleJson}, 95 | )) 96 | ); 97 | 98 | test("test not", () => 99 | expect(r(not_("p", [ruleSelector]))->Js.Json.stringifyAny) 100 | |> toBeJson({":not(p)": ruleJson}) 101 | ); 102 | 103 | test("test nth-child", () => 104 | expect( 105 | ( 106 | r(nthChild(`odd, [ruleSelector])), 107 | r(nthChild(`even, [ruleSelector])), 108 | r(nthChild(`n(2), [ruleSelector])), 109 | r(nthChild(`add((3, 4)), [ruleSelector])), 110 | ) 111 | ->Js.Json.stringifyAny, 112 | ) 113 | |> toBeJson(( 114 | {":nth-child(odd)": ruleJson}, 115 | {":nth-child(even)": ruleJson}, 116 | {":nth-child(2n)": ruleJson}, 117 | {":nth-child(3n+4)": ruleJson}, 118 | )) 119 | ); 120 | 121 | test("test nth-last-child", () => 122 | expect( 123 | ( 124 | r(nthLastChild(`odd, [ruleSelector])), 125 | r(nthLastChild(`even, [ruleSelector])), 126 | r(nthLastChild(`n(2), [ruleSelector])), 127 | r(nthLastChild(`add((3, 4)), [ruleSelector])), 128 | ) 129 | ->Js.Json.stringifyAny, 130 | ) 131 | |> toBeJson(( 132 | {":nth-last-child(odd)": ruleJson}, 133 | {":nth-last-child(even)": ruleJson}, 134 | {":nth-last-child(2n)": ruleJson}, 135 | {":nth-last-child(3n+4)": ruleJson}, 136 | )) 137 | ); 138 | 139 | test("test nth-last-of-type", () => 140 | expect( 141 | ( 142 | r(nthLastOfType(`odd, [ruleSelector])), 143 | r(nthLastOfType(`even, [ruleSelector])), 144 | r(nthLastOfType(`n(2), [ruleSelector])), 145 | r(nthLastOfType(`add((3, 4)), [ruleSelector])), 146 | ) 147 | ->Js.Json.stringifyAny, 148 | ) 149 | |> toBeJson(( 150 | {":nth-last-of-type(odd)": ruleJson}, 151 | {":nth-last-of-type(even)": ruleJson}, 152 | {":nth-last-of-type(2n)": ruleJson}, 153 | {":nth-last-of-type(3n+4)": ruleJson}, 154 | )) 155 | ); 156 | 157 | test("test nth-of-type", () => 158 | expect( 159 | ( 160 | r(nthOfType(`odd, [ruleSelector])), 161 | r(nthOfType(`even, [ruleSelector])), 162 | r(nthOfType(`n(2), [ruleSelector])), 163 | r(nthOfType(`add((3, 4)), [ruleSelector])), 164 | ) 165 | ->Js.Json.stringifyAny, 166 | ) 167 | |> toBeJson(( 168 | {":nth-of-type(odd)": ruleJson}, 169 | {":nth-of-type(even)": ruleJson}, 170 | {":nth-of-type(2n)": ruleJson}, 171 | {":nth-of-type(3n+4)": ruleJson}, 172 | )) 173 | ); 174 | }); 175 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astrada/bs-css-core", 3 | "namespace": "bs-css-core", 4 | "refmt": 3, 5 | "warnings": { 6 | "number": "-32-26-44" 7 | }, 8 | "bs-dev-dependencies": ["@glennsl/bs-jest"], 9 | "sources": [ 10 | { 11 | "dir": "src" 12 | }, 13 | { 14 | "dir": "__tests__", 15 | "type": "dev" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /docs/block-script.js: -------------------------------------------------------------------------------- 1 | const Colors = (function() { 2 | const O = Object 3 | 4 | /* See https://misc.flogisoft.com/bash/tip_colors_and_formatting 5 | ------------------------------------------------------------------------ */ 6 | 7 | const colorCodes = [ 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'lightGray', '', 'default'] 8 | , colorCodesLight = ['darkGray', 'lightRed', 'lightGreen', 'lightYellow', 'lightBlue', 'lightMagenta', 'lightCyan', 'white', ''] 9 | 10 | , styleCodes = ['', 'bright', 'dim', 'italic', 'underline', '', '', 'inverse'] 11 | 12 | , asBright = { 'red': 'lightRed', 13 | 'green': 'lightGreen', 14 | 'yellow': 'lightYellow', 15 | 'blue': 'lightBlue', 16 | 'magenta': 'lightMagenta', 17 | 'cyan': 'lightCyan', 18 | 'black': 'darkGray', 19 | 'lightGray': 'white' } 20 | 21 | , types = { 0: 'style', 22 | 2: 'unstyle', 23 | 3: 'color', 24 | 9: 'colorLight', 25 | 4: 'bgColor', 26 | 10: 'bgColorLight' } 27 | 28 | , subtypes = { color: colorCodes, 29 | colorLight: colorCodesLight, 30 | bgColor: colorCodes, 31 | bgColorLight: colorCodesLight, 32 | style: styleCodes, 33 | unstyle: styleCodes } 34 | 35 | /* ------------------------------------------------------------------------ */ 36 | 37 | const clean = obj => { 38 | for (const k in obj) { if (!obj[k]) { delete obj[k] } } 39 | return (O.keys (obj).length === 0) ? undefined : obj 40 | } 41 | 42 | /* ------------------------------------------------------------------------ */ 43 | 44 | class Color { 45 | 46 | constructor (background, name, brightness) { 47 | 48 | this.background = background 49 | this.name = name 50 | this.brightness = brightness 51 | } 52 | 53 | get inverse () { 54 | return new Color (!this.background, this.name || (this.background ? 'black' : 'white'), this.brightness) 55 | } 56 | 57 | get clean () { 58 | return clean ({ name: this.name === 'default' ? '' : this.name, 59 | bright: this.brightness === Code.bright, 60 | dim: this.brightness === Code.dim }) 61 | } 62 | 63 | defaultBrightness (value) { 64 | 65 | return new Color (this.background, this.name, this.brightness || value) 66 | } 67 | 68 | css (inverted) { 69 | 70 | const color = inverted ? this.inverse : this 71 | 72 | const rgbName = ((color.brightness === Code.bright) && asBright[color.name]) || color.name 73 | 74 | const prop = (color.background ? 'background:' : 'color:') 75 | , rgb = Colors.rgb[rgbName] 76 | , alpha = (this.brightness === Code.dim) ? 0.5 : 1 77 | 78 | return rgb 79 | ? (prop + 'rgba(' + [...rgb, alpha].join (',') + ');') 80 | : ((!color.background && (alpha < 1)) ? 'color:rgba(0,0,0,0.5);' : '') // Chrome does not support 'opacity' property... 81 | } 82 | } 83 | 84 | /* ------------------------------------------------------------------------ */ 85 | 86 | class Code { 87 | 88 | constructor (n) { 89 | if (n !== undefined) { this.value = Number (n) } } 90 | 91 | get type () { 92 | return types[Math.floor (this.value / 10)] } 93 | 94 | get subtype () { 95 | return subtypes[this.type][this.value % 10] } 96 | 97 | get str () { 98 | return (this.value ? ('\u001b\[' + this.value + 'm') : '') } 99 | 100 | static str (x) { 101 | return new Code (x).str } 102 | 103 | get isBrightness () { 104 | return (this.value === Code.noBrightness) || (this.value === Code.bright) || (this.value === Code.dim) } 105 | } 106 | 107 | /* ------------------------------------------------------------------------ */ 108 | 109 | O.assign (Code, { 110 | 111 | bright: 1, 112 | dim: 2, 113 | inverse: 7, 114 | noBrightness: 22, 115 | noItalic: 23, 116 | noUnderline: 24, 117 | noInverse: 27, 118 | noColor: 39, 119 | noBgColor: 49 120 | }) 121 | 122 | /* ------------------------------------------------------------------------ */ 123 | 124 | const replaceAll = (str, a, b) => str.split (a).join (b) 125 | 126 | /* ANSI brightness codes do not overlap, e.g. "{bright}{dim}foo" will be rendered bright (not dim). 127 | So we fix it by adding brightness canceling before each brightness code, so the former example gets 128 | converted to "{noBrightness}{bright}{noBrightness}{dim}foo" – this way it gets rendered as expected. 129 | */ 130 | 131 | const denormalizeBrightness = s => s.replace (/(\u001b\[(1|2)m)/g, '\u001b[22m$1') 132 | const normalizeBrightness = s => s.replace (/\u001b\[22m(\u001b\[(1|2)m)/g, '$1') 133 | 134 | const wrap = (x, openCode, closeCode) => { 135 | 136 | const open = Code.str (openCode), 137 | close = Code.str (closeCode) 138 | 139 | return String (x) 140 | .split ('\n') 141 | .map (line => denormalizeBrightness (open + replaceAll (normalizeBrightness (line), close, open) + close)) 142 | .join ('\n') 143 | } 144 | 145 | /* ------------------------------------------------------------------------ */ 146 | 147 | const camel = (a, b) => a + b.charAt (0).toUpperCase () + b.slice (1) 148 | 149 | 150 | const stringWrappingMethods = (() => [ 151 | 152 | ...colorCodes.map ((k, i) => !k ? [] : [ // color methods 153 | 154 | [k, 30 + i, Code.noColor], 155 | [camel ('bg', k), 40 + i, Code.noBgColor], 156 | ]), 157 | 158 | ...colorCodesLight.map ((k, i) => !k ? [] : [ // light color methods 159 | 160 | [k, 90 + i, Code.noColor], 161 | [camel ('bg', k), 100 + i, Code.noBgColor], 162 | ]), 163 | 164 | /* THIS ONE IS FOR BACKWARDS COMPATIBILITY WITH PREVIOUS VERSIONS (had 'bright' instead of 'light' for backgrounds) 165 | */ 166 | ...['', 'BrightRed', 'BrightGreen', 'BrightYellow', 'BrightBlue', 'BrightMagenta', 'BrightCyan'].map ((k, i) => !k ? [] : [ 167 | 168 | ['bg' + k, 100 + i, Code.noBgColor], 169 | ]), 170 | 171 | ...styleCodes.map ((k, i) => !k ? [] : [ // style methods 172 | 173 | [k, i, ((k === 'bright') || (k === 'dim')) ? Code.noBrightness : (20 + i)] 174 | ]) 175 | ] 176 | .reduce ((a, b) => a.concat (b)) 177 | 178 | ) (); 179 | 180 | /* ------------------------------------------------------------------------ */ 181 | 182 | const assignStringWrappingAPI = (target, wrapBefore = target) => 183 | 184 | stringWrappingMethods.reduce ((memo, [k, open, close]) => 185 | O.defineProperty (memo, k, { 186 | get: () => assignStringWrappingAPI (str => wrapBefore (wrap (str, open, close))) 187 | }), 188 | 189 | target) 190 | 191 | /* ------------------------------------------------------------------------ */ 192 | 193 | /** 194 | * Represents an ANSI-escaped string. 195 | */ 196 | class Colors { 197 | 198 | /** 199 | * @param {string} s a string containing ANSI escape codes. 200 | */ 201 | constructor (s) { 202 | 203 | if (s) { 204 | 205 | const r = /\u001b\[(\d+)m/g 206 | 207 | const spans = s.split (/\u001b\[\d+m/) 208 | const codes = [] 209 | 210 | for (let match; match = r.exec (s);) codes.push (match[1]) 211 | 212 | this.spans = spans.map ((s, i) => ({ text: s, code: new Code (codes[i]) })) 213 | } 214 | 215 | else { 216 | this.spans = [] 217 | } 218 | } 219 | 220 | get str () { 221 | return this.spans.reduce ((str, p) => str + p.text + p.code.str, '') 222 | } 223 | 224 | get parsed () { 225 | 226 | var color = new Color (), 227 | bgColor = new Color (true /* background */), 228 | brightness = undefined, 229 | styles = new Set () 230 | 231 | return O.assign (new Colors (), { 232 | 233 | spans: this.spans.map (span => { 234 | 235 | const c = span.code 236 | 237 | const inverted = styles.has ('inverse'), 238 | underline = styles.has ('underline') ? 'text-decoration: underline;' : '', 239 | italic = styles.has ('italic') ? 'font-style: italic;' : '', 240 | bold = brightness === Code.bright ? 'font-weight: bold;' : '' 241 | 242 | const foreColor = color.defaultBrightness (brightness) 243 | 244 | const styledSpan = O.assign ( 245 | { css: bold + italic + underline + foreColor.css (inverted) + bgColor.css (inverted) }, 246 | clean ({ bold: !!bold, color: foreColor.clean, bgColor: bgColor.clean }), 247 | span) 248 | 249 | for (const k of styles) { styledSpan[k] = true } 250 | 251 | if (c.isBrightness) { 252 | 253 | brightness = c.value 254 | 255 | } else { 256 | 257 | switch (span.code.type) { 258 | 259 | case 'color' : 260 | case 'colorLight' : color = new Color (false, c.subtype); break 261 | 262 | case 'bgColor' : 263 | case 'bgColorLight' : bgColor = new Color (true, c.subtype); break 264 | 265 | case 'style' : styles.add (c.subtype); break 266 | case 'unstyle': styles.delete (c.subtype); break 267 | } 268 | } 269 | 270 | return styledSpan 271 | 272 | }).filter (s => s.text.length > 0) 273 | }) 274 | } 275 | 276 | /* Outputs with Chrome DevTools-compatible format */ 277 | 278 | get asChromeConsoleLogArguments () { 279 | 280 | const spans = this.parsed.spans 281 | 282 | return [spans.map (s => ('%c' + s.text)).join (''), 283 | ...spans.map (s => s.css)] 284 | } 285 | 286 | get browserConsoleArguments () /* LEGACY, DEPRECATED */ { return this.asChromeConsoleLogArguments } 287 | 288 | /** 289 | * @desc installs String prototype extensions 290 | * @example 291 | * require ('ansicolor').nice 292 | * console.log ('foo'.bright.red) 293 | */ 294 | static get nice () { 295 | 296 | Colors.names.forEach (k => { 297 | if (!(k in String.prototype)) { 298 | O.defineProperty (String.prototype, k, { get: function () { return Colors[k] (this) } }) 299 | } 300 | }) 301 | 302 | return Colors 303 | } 304 | 305 | /** 306 | * @desc parses a string containing ANSI escape codes 307 | * @return {Colors} parsed representation. 308 | */ 309 | static parse (s) { 310 | return new Colors (s).parsed 311 | } 312 | 313 | /** 314 | * @desc strips ANSI codes from a string 315 | * @param {string} s a string containing ANSI escape codes. 316 | * @return {string} clean string. 317 | */ 318 | static strip (s) { 319 | return s.replace (/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]/g, '') // hope V8 caches the regexp 320 | } 321 | 322 | /** 323 | * @example 324 | * const spans = [...ansi.parse ('\u001b[7m\u001b[7mfoo\u001b[7mbar\u001b[27m')] 325 | */ 326 | [Symbol.iterator] () { 327 | return this.spans[Symbol.iterator] () 328 | } 329 | } 330 | 331 | /* ------------------------------------------------------------------------ */ 332 | 333 | assignStringWrappingAPI (Colors, str => str) 334 | 335 | /* ------------------------------------------------------------------------ */ 336 | 337 | Colors.names = stringWrappingMethods.map (([k]) => k) 338 | 339 | /* ------------------------------------------------------------------------ */ 340 | 341 | Colors.rgb = { 342 | 343 | black: [0, 0, 0], 344 | darkGray: [100, 100, 100], 345 | lightGray: [200, 200, 200], 346 | white: [255, 255, 255], 347 | 348 | red: [204, 0, 0], 349 | lightRed: [255, 51, 0], 350 | 351 | green: [0, 204, 0], 352 | lightGreen: [51, 204, 51], 353 | 354 | yellow: [204, 102, 0], 355 | lightYellow: [255, 153, 51], 356 | 357 | blue: [0, 0, 255], 358 | lightBlue: [26, 140, 255], 359 | 360 | magenta: [204, 0, 204], 361 | lightMagenta: [255, 0, 255], 362 | 363 | cyan: [0, 153, 255], 364 | lightCyan: [0, 204, 255], 365 | } 366 | 367 | /* ------------------------------------------------------------------------ */ 368 | 369 | return Colors 370 | 371 | /* ------------------------------------------------------------------------ */ 372 | })(); 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | (function() { 384 | 385 | var node = (tag, attrs, children) => { 386 | var node = document.createElement(tag) 387 | for (var attr in attrs) { 388 | if (attr === 'style') { 389 | Object.assign(node.style, attrs[attr]) 390 | } else if (attr === 'onclick') { 391 | node.onclick = attrs[attr] 392 | } else { 393 | node.setAttribute(attr, attrs[attr]) 394 | } 395 | } 396 | children && children.forEach(child => node.appendChild(typeof child === 'string' ? document.createTextNode(child) : child)) 397 | return node 398 | } 399 | var named = tag => (attrs, children) => node(tag, attrs, children) 400 | var div = named('div') 401 | var span = named('span') 402 | var a = named('a') 403 | var raw = text => { 404 | var node = document.createElement('div') 405 | node.innerHTML = text 406 | return node 407 | }; 408 | 409 | var render = (target, node) => { 410 | target.innerHTML = '' 411 | target.appendChild(node) 412 | }; 413 | 414 | 415 | const loadScript = absPath => { 416 | return new Promise((res, rej) => { 417 | const src = window.relativeToRoot + '/' + absPath; 418 | const script = node('script', {src}); 419 | script.onload = () => res(); 420 | script.onerror = e => rej(e); 421 | document.body.appendChild(script) 422 | }) 423 | } 424 | 425 | const loadCss = absPath => { 426 | return new Promise((res, rej) => { 427 | const href = window.relativeToRoot + '/' + absPath; 428 | const link = node('link', {href, rel: 'stylesheet'}); 429 | link.onload = () => res(); 430 | link.onerror = e => rej(e); 431 | document.head.appendChild(link) 432 | }) 433 | } 434 | 435 | const memoCss = absPath => { 436 | let promise = null 437 | return () => (promise || (promise = loadCss(absPath))) 438 | }; 439 | 440 | const memoLoad = absPath => { 441 | let promise = null 442 | return () => (promise || (promise = loadScript(absPath))) 443 | }; 444 | 445 | const loadDeps = memoLoad('all-deps.js'); 446 | const loadJsx = memoLoad('jsx-ppx.js'); 447 | const loadRefmt = memoLoad('refmt.js'); 448 | const loadOcaml = memoLoad('bucklescript.js'); 449 | const loadOcamlDeps = memoLoad('bucklescript-deps.js'); 450 | const loadCodeMirror = memoLoad('codemirror.js'); 451 | const loadRust = memoLoad('rust.js'); 452 | const loadSimple = memoLoad('simple.js'); 453 | const loadCodeMirrorCss = memoCss('codemirror.css'); 454 | const loadAll = () => Promise.all([loadJsx(), loadRefmt(), loadDeps(), loadOcaml().then(() => loadOcamlDeps())]) 455 | 456 | const runSandboxed = (script, logs, context) => { 457 | const addLog = (level, items) => { 458 | var text = '' 459 | if (items.length === 1 && typeof items[0] === 'string') { 460 | text = items[0] 461 | } else { 462 | text = items.map(item => JSON.stringify(item)).join(' ') 463 | } 464 | logs.appendChild(div({class: 'block-log level-' + level}, [text])) 465 | }; 466 | 467 | const oldConsole = window.console 468 | window.console = Object.assign({}, window.console, { 469 | log: (...items) => {oldConsole.log(...items); addLog('log', items)}, 470 | warn: (...items) => {oldConsole.warn(...items); addLog('warn', items)}, 471 | error: (...items) => {oldConsole.error(...items); addLog('error', items)}, 472 | }); 473 | Object.assign(window, context) 474 | // ok folks we're done 475 | const exports = {} 476 | const module = {exports} 477 | const require = name => { 478 | return bsRequirePaths[name] ? window.packRequire(bsRequirePaths[name]) : window.packRequire(name) 479 | } 480 | try { 481 | // TODO check if it's a promise or something... and maybe wait? 482 | eval(script); 483 | } catch (error) { 484 | oldConsole.error(error) 485 | addLog('error', [error && error.message || error]) 486 | } 487 | window.console = oldConsole 488 | for (let name in context) { 489 | window[name] = null 490 | } 491 | }; 492 | 493 | var initBlocks = () => { 494 | ;[].forEach.call(document.querySelectorAll('div.block-target'), el => { 495 | const viewContext = el.getAttribute('data-context'); 496 | const id = el.getAttribute('data-block-id'); 497 | const parent = el.parentNode; 498 | const syntax = el.getAttribute('data-block-syntax'); 499 | 500 | const logs = div({class: 'block-logs'}, []); 501 | 502 | const bundleScript = document.querySelector('script[type=redoc-bundle][data-block-id="' + id + '"]') 503 | const sourceScript = document.querySelector('script[type=redoc-source][data-block-id="' + id + '"]') 504 | 505 | if (!bundleScript) { 506 | // not runnable, not editable 507 | return 508 | } 509 | 510 | let ran = false 511 | 512 | window.process = {env: {NODE_ENV: 'production'}} 513 | 514 | const runBlock = (context) => { 515 | if (ran) { 516 | return Promise.resolve() 517 | } 518 | ran = true 519 | return loadDeps().then(() => { 520 | console.log(id) 521 | if (!bundleScript) { 522 | console.error('bundle not found') 523 | return 524 | } 525 | runSandboxed(bundleScript.textContent, logs, context); 526 | }) 527 | } 528 | let context = {} 529 | 530 | const betterShiftTab = /*onInfo => */cm => { 531 | var cursor = cm.getCursor() 532 | , line = cm.getLine(cursor.line) 533 | , pos = {line: cursor.line, ch: cursor.ch} 534 | cm.execCommand('indentLess') 535 | } 536 | 537 | const betterTab = /*onComplete => */cm => { 538 | if (cm.somethingSelected()) { 539 | return cm.indentSelection("add"); 540 | } 541 | const cursor = cm.getCursor() 542 | const line = cm.getLine(cursor.line) 543 | const pos = {line: cursor.line, ch: cursor.ch} 544 | cm.replaceSelection(Array(cm.getOption("indentUnit") + 1).join(" "), "end", "+input"); 545 | } 546 | 547 | const htmlEscape = message => message 548 | .replace('&', '&') 549 | .replace('<', '<') 550 | .replace('>', '>'); 551 | 552 | const fixEscapes = message => { 553 | return Colors.parse(message 554 | .replace(new RegExp('\u001b\\[1;', 'g'), '\u001b[') 555 | .replace(new RegExp('\u001b\\[0m', 'g'), '\u001b[39m') 556 | ).spans.map(span => `${htmlEscape(span.text)}`).join('') 557 | }; 558 | 559 | const getJs = (result, showError) => { 560 | if (!result) return 561 | try { 562 | if (typeof result === 'string') { 563 | // bs 2.2.3 564 | result = JSON.parse(result) 565 | } 566 | const {js_code, js_error_msg, row, column, endRow, endColumn} = result 567 | if (!js_code && js_error_msg) { 568 | // TODO: just compile the straight reason, so these numbers mean something 569 | if (showError) { 570 | showError(row, column, endRow, endColumn) 571 | } 572 | error.innerHTML = fixEscapes(js_error_msg) 573 | error.style.display = 'block' 574 | console.log(result) 575 | return 576 | } 577 | return js_code 578 | } catch (e) { 579 | console.error(e) 580 | error.textContent = e.message 581 | error.style.display = 'block' 582 | return 583 | } 584 | } 585 | 586 | const preprocessReason = code => { 587 | let ocaml 588 | try { 589 | ocaml = window.printML(window.parseRE(code)) 590 | } catch (e) { 591 | if (e.location) { 592 | showError(e.location.startLine - 1, e.location.startLineStartChar - 1, e.location.endLine - 1, e.location.endLineEndChar - 1) 593 | } 594 | console.error(e) 595 | error.textContent = e.message 596 | error.style.display = 'block' 597 | return 598 | } 599 | let ppxed 600 | try { 601 | const {ppx_error_msg, js_error_msg, ocaml_code} = window.jsxv2.rewrite(ocaml) 602 | if (ppx_error_msg || js_error_msg) { 603 | console.error(ppx_error_msg, js_error_msg) 604 | error.textContent = (ppx_error_msg || '') + ' ' + (js_error_msg || '') 605 | error.style.display = 'block' 606 | return 607 | } 608 | return ocaml_code 609 | } catch (e) { 610 | console.error(e) 611 | error.textContent = e.message 612 | error.style.display = 'block' 613 | return 614 | } 615 | } 616 | 617 | const execute = (cm, code, before) => { 618 | logs.innerHTML = '' 619 | cm.getAllMarks().forEach(mark => { 620 | cm.removeLineWidget(mark) 621 | }) 622 | const showError = (l1, c1, l2, c2) => { 623 | c2 = l1 === l2 && c1 === c2 ? c2 + 1 : c2; 624 | console.log('showing', l1, c1, l2, c2) 625 | cm.markText({line: l1 - before, ch: c1}, {line: l2 - before, ch: c2}, { 626 | className: 'CodeMirror-error-mark', 627 | }) 628 | } 629 | return loadAll().then(() => { 630 | const js = syntax == 'ml' 631 | ? getJs(window.ocaml.compile(code), showError) 632 | : (window.ocaml.reason_compile_super_errors 633 | ? getJs(window.ocaml.reason_compile_super_errors(code), showError) 634 | : getJs(window.ocaml.compile(preprocessReason(code)))) 635 | if (js) { 636 | error.style.display = 'none' 637 | runSandboxed(js, logs, Object.assign({}, context)) 638 | } 639 | }) 640 | } 641 | 642 | const pre = document.querySelector('pre.code[data-block-id="' + id + '"]') 643 | const error = div({class: 'code-block-error', style: {display: 'none'}}) 644 | pre.insertAdjacentElement('afterend', error) 645 | 646 | const processHashes = code => { 647 | const lines = code.split('\n') 648 | let before = 0 649 | let after = 0 650 | for (let i=0; i m.replace(/^!?#/, '')).join('\n') + (before > 0 ? '\n' : ''), 670 | mainCode: lines.slice(before, lines.length - after).join('\n'), 671 | suffix: (after > 0 ? '\n' : '') + lines.slice(lines.length - after).map(m => m.replace(/^!?#/, '')).join('\n'), 672 | } 673 | } 674 | 675 | let onEditRun = () => {}; 676 | 677 | let loadingIcon = "⋯"; 678 | let playIcon = "▶"; 679 | 680 | let playButton 681 | if (viewContext === 'canvas') { 682 | playButton = div({class: 'block-canvas-play'}, ["▶"]) 683 | const canvas = node('canvas', {id: 'block-canvas-' + id}) 684 | canvas.width = 200 685 | canvas.height = 200 686 | context = {sandboxCanvas: canvas, sandboxCanvasId: canvas.id, containerDiv: parent} 687 | playButton.onclick = () => { 688 | playButton.textContent = loadingIcon 689 | runBlock(context).then(() => { 690 | playButton.style.display = 'none' 691 | }) 692 | } 693 | const canvasBlock = div({class: 'block-canvas-container'}, [ 694 | canvas, 695 | playButton 696 | ]); 697 | parent.appendChild(canvasBlock) 698 | } else if (viewContext === 'div') { 699 | const target = div({id: 'block-target-div-' + id}) 700 | const container = div({class: 'block-target-container'}, [target]) 701 | parent.appendChild(container) 702 | playButton = div({class: 'block-target-right'}, [playIcon]) 703 | context = {sandboxDiv: target, sandboxDivId: target.id, containerDiv: parent} 704 | onEditRun = () => container.classList.add('active') 705 | playButton.onclick = () => { 706 | playButton.textContent = loadingIcon 707 | runBlock(context).then(() => { 708 | playButton.style.display = 'none' 709 | container.classList.add('active') 710 | }) 711 | } 712 | parent.appendChild(playButton) 713 | } else { 714 | playButton = div({class: 'block-target-right'}, [playIcon]) 715 | playButton.onclick = () => { 716 | playButton.textContent = loadingIcon 717 | runBlock({containerDiv: parent}).then(() => { 718 | playButton.style.display = 'none' 719 | }) 720 | } 721 | parent.appendChild(playButton) 722 | } 723 | 724 | parent.appendChild(logs) 725 | 726 | if (!sourceScript) { 727 | // not editable 728 | return 729 | } 730 | const editButton = node('button', {class: 'code-edit-button'}, ["Edit"]); 731 | pre.appendChild(editButton) 732 | 733 | const startEditing = () => { 734 | editButton.textContent = loadingIcon 735 | if (playButton && playButton.parentNode) { 736 | playButton.parentNode.removeChild(playButton) 737 | playButton = null 738 | } 739 | // const code = sourceScript.textContent 740 | const {before, prefix, mainCode, suffix} = processHashes(sourceScript.textContent) 741 | console.log([prefix, mainCode, suffix]) 742 | 743 | 744 | Promise.all([loadCodeMirror().then(() => loadSimple()).then(() => loadRust()), loadCodeMirrorCss()]).then(() => { 745 | const textarea = node('textarea', {class: 'code-block-editor', style: {width: '100%'}}) 746 | pre.replaceWith(textarea) 747 | textarea.value = mainCode 748 | let playButton 749 | 750 | const run = (cm) => { 751 | onEditRun(); 752 | playButton.textContent = loadingIcon 753 | execute(cm, prefix + '#1 ""\n' + cm.getValue() + suffix, 0).then(() => { 754 | playButton.textContent = playIcon 755 | }) 756 | } 757 | 758 | const cm = CodeMirror.fromTextArea(textarea, { 759 | lineNumbers: true, 760 | lineWrapping: true, 761 | viewportMargin: Infinity, 762 | extraKeys: { 763 | 'Cmd-Enter': (cm) => run(cm), 764 | 'Ctrl-Enter': (cm) => run(cm), 765 | Tab: betterTab, 766 | 'Shift-Tab': betterShiftTab, 767 | }, 768 | mode: 'rust', 769 | }) 770 | 771 | playButton = node('button', {class: 'code-edit-run'}, ["▶"]) 772 | playButton.onclick = () => run(cm) 773 | parent.appendChild(playButton) 774 | }) 775 | } 776 | editButton.addEventListener('click', startEditing) 777 | 778 | }) 779 | 780 | var style = document.createElement('style') 781 | document.head.appendChild(style) 782 | var show = lang => { 783 | localStorage.preferredSyntax = lang 784 | style.innerText = ` 785 | .syntax-buttons { 786 | position: fixed; 787 | top: 0; 788 | right: 0; 789 | padding: 4px 8px; 790 | z-index: 1000; 791 | background: rgba(255,255,255,0.8); 792 | border-bottom-left-radius: 4px; 793 | } 794 | @media (max-width: 1000px) { 795 | .syntax-buttons { 796 | position: absolute; 797 | } 798 | } 799 | 800 | .syntax-buttons button { 801 | background: white; 802 | border: none; 803 | border-radius: 3px; 804 | padding: 4px 8px; 805 | margin: 0 4px; 806 | } 807 | .button-picker-${lang} { 808 | background-color: #eee; 809 | font-weight: bold; 810 | } 811 | div.code-block { display: none } div.code-block[data-block-syntax=${lang}] { display: block } 812 | .code-block[data-block-syntax=ml] pre > code::after { 813 | content: 'ml'; 814 | position: absolute; 815 | bottom: 0; 816 | right: 4px; 817 | color: #999; 818 | pointer-events: none; 819 | } 820 | .code-block[data-block-syntax=re] pre > code::after { 821 | content: 're'; 822 | position: absolute; 823 | bottom: 0; 824 | right: 4px; 825 | color: #999; 826 | pointer-events: none; 827 | } 828 | .code-block pre { 829 | position: relative; 830 | } 831 | ` 832 | } 833 | let lang = localStorage.preferredSyntax || 're' 834 | show(lang) 835 | var buttons = div({class: 'syntax-buttons', style: { 836 | }}, [ 837 | 'Syntax: ', 838 | node('button', {class: 'button-picker-ml', onclick: () => show('ml')}, ['ocaml']), 839 | node('button', {class: 'button-picker-re', onclick: () => show('re')}, ['reason']), 840 | ]); 841 | document.body.appendChild(buttons) 842 | } 843 | 844 | window.addEventListener('load', () => { 845 | initBlocks(); 846 | }) 847 | 848 | })(); 849 | -------------------------------------------------------------------------------- /docs/elasticlunr.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * elasticlunr - http://weixsong.github.io 4 | * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 5 | * 6 | * Copyright (C) 2017 Oliver Nightingale 7 | * Copyright (C) 2017 Wei Song 8 | * MIT Licensed 9 | * @license 10 | */ 11 | !function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o 4 | 5 | 6 | 7 | 8 | Home 9 | 10 | 11 | 12 |
13 | ⚠️ Oops! This page doesn't appear to define a type called _. 14 |
15 | 16 | 17 |
18 | 19 | 38 | 39 |
40 | Edit 41 |

bs-css-core

42 | 43 |

Statically typed DSL for writing CSS in ReasonML.

44 |

This is a fork of bs-css that 45 | extracts the Css module, so that it can be used by various CSS-in-JS bindings. 46 | See bs-react-fela-examples 47 | and 48 | bs-styletron-react-examples 49 | for practical examples of usage.

50 |

Installation

51 | 52 |
yarn add @astrada/bs-css-core
53 | 54 |

In your bsconfig.json, include "@astrada/bs-css-core" in the 55 | bs-dependencies.

56 |

Usage

57 | 58 |
59 | 60 |
type theme = {
 61 |   textColor: Css.color,
 62 |   basePadding: Css.length
 63 | };
 64 | 
 65 | let makeStyle = (theme) =>
 66 |   Css.(
 67 |     style([
 68 |       backgroundColor(white),
 69 |       boxShadows([boxShadow(~y=px(3), ~blur=px(5), rgba(0, 0, 0, 0.3))]),
 77 |       padding(theme.basePadding),
 78 |       fontSize(rem(1.5)),
117 |       color(theme.textColor),
118 |       marginBottom(px(10))
155 |     ])
156 |   );
157 | 158 | 159 | 160 |
161 | 162 |
[@@@ocaml.ppx.context { cookies = [] }]
163 | type theme = {
164 |   textColor: Css.color;
165 |   basePadding: Css.length;}
166 | let makeStyle theme =
167 |   let open Css in
168 |     style
169 |       [backgroundColor white;
170 |       boxShadows [boxShadow ~y:(px 3) ~blur:(px 5) (rgba 0 0 0 0.3)];
178 |       padding theme.basePadding;
179 |       fontSize (rem 1.5);
218 |       color theme.textColor;
219 |       marginBottom (px 10)]
256 | 257 | 258 | 259 |
260 | 261 |

Keyframes

262 |

Define animation keyframes;

263 |
264 | 265 |
let bounce =
266 |   Css.(
267 |     keyframes([
268 |       (0, [transform(scale(0.1, 0.1)), opacity(0.0)]),
269 |       (60, [transform(scale(1.2, 1.2)), opacity(1.0)]),
270 |       (100, [transform(scale(1.0, 1.0)), opacity(1.0)])
271 |     ])
272 |   );
273 | 
274 | let makeStyle = (_theme) =>
275 |   Css.(
276 |     style([
277 |       animationName(bounce),
278 |       animationDuration(2000),
279 |       width(px(50)),
316 |       height(px(50)),
353 |       backgroundColor(rgb(255, 0, 0))
354 |     ])
355 |   );
356 | 357 | 358 | 359 |
360 | 361 |
[@@@ocaml.ppx.context { cookies = [] }]
362 | let bounce =
363 |   let open Css in
364 |     keyframes
365 |       [(0, [transform (scale 0.1 0.1); opacity 0.0]);
366 |       (60, [transform (scale 1.2 1.2); opacity 1.0]);
367 |       (100, [transform (scale 1.0 1.0); opacity 1.0])]
368 | let makeStyle _theme =
369 |   let open Css in
370 |     style
371 |       [animationName bounce;
372 |       animationDuration 2000;
373 |       width (px 50);
410 |       height (px 50);
447 |       backgroundColor (rgb 255 0 0)]
448 | 449 | 450 | 451 |
452 | 453 |

Development

454 | 455 |
yarn start
456 | 457 |

Where is the documentation?

458 | 459 |

Documentation generated with redoc is 460 | published here.

461 |

Thanks

462 | 463 |

Thanks to bs-css that introduced 464 | the DSL. Thanks to bs-glamor which 465 | bs-css was forked from. Thanks to 466 | elm-css for DSL design inspiration. 467 | Thanks to @jaredly for redoc.

468 | 469 |
470 |
471 |
472 | -------------------------------------------------------------------------------- /docs/script.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var node = (tag, attrs, children) => { 3 | var node = document.createElement(tag) 4 | for (var attr in attrs) { 5 | if (attr === 'style') { 6 | Object.assign(node.style, attrs[attr]) 7 | } else { 8 | node.setAttribute(attr, attrs[attr]) 9 | } 10 | } 11 | children && children.forEach(child => node.appendChild(typeof child === 'string' ? document.createTextNode(child) : child)) 12 | return node 13 | } 14 | var named = tag => (attrs, children) => node(tag, attrs, children) 15 | var div = named('div') 16 | var span = named('span') 17 | var a = named('a') 18 | var raw = text => { 19 | var node = document.createElement('div') 20 | node.innerHTML = text 21 | return node 22 | }; 23 | 24 | var render = (target, node) => { 25 | target.innerHTML = '' 26 | target.appendChild(node) 27 | }; 28 | 29 | var listenForTypes = () => { 30 | var typeViewer = document.createElement('div') 31 | typeViewer.className = 'type-viewer' 32 | document.body.appendChild(typeViewer) 33 | ;[].forEach.call(document.querySelectorAll('pre.code'), el => { 34 | el.addEventListener('mousemove', evt => { 35 | typeViewer.style.top = evt.pageY + 'px' 36 | typeViewer.style.left = evt.pageX + 'px' 37 | }); 38 | el.addEventListener('mouseover', evt => { 39 | if (evt.target.getAttribute('data-type')) { 40 | evt.target.classList.add('type-hovered') 41 | typeViewer.textContent = evt.target.getAttribute('data-type') 42 | typeViewer.style.display = 'block' 43 | } 44 | }) 45 | el.addEventListener('mouseout', evt => { 46 | if (evt.target.getAttribute('data-type')) { 47 | evt.target.classList.remove('type-hovered') 48 | typeViewer.style.display = 'none' 49 | } 50 | }) 51 | }) 52 | } 53 | 54 | var checkHash = () => { 55 | if (!window.shouldCheckHashes) { 56 | return 57 | } 58 | var id = window.location.hash.slice(1) 59 | if (id && !document.getElementById(id)) { 60 | document.getElementById("error-message").style.display = 'block' 61 | var parts = id.split('-') 62 | document.querySelector('#error-message span').textContent = parts[0] 63 | document.querySelector('#error-message code').textContent = parts[1] 64 | } else { 65 | document.getElementById("error-message").style.display = 'none' 66 | } 67 | } 68 | window.onload = () => { 69 | checkHash() 70 | var expander = document.querySelector('.sidebar-expander') 71 | expander.onclick = () => { 72 | var sidebar = document.querySelector('.sidebar'); 73 | if (sidebar.classList.contains('expanded')) { 74 | sidebar.classList.remove('expanded') 75 | expander.textContent = 'Show navigation' 76 | } else { 77 | sidebar.classList.add('expanded') 78 | expander.textContent = 'Hide navigation' 79 | } 80 | } 81 | listenForTypes(); 82 | } 83 | window.onhashchange = checkHash 84 | })(); -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Search 9 | 10 | 11 | 12 |
13 | ⚠️ Oops! This page doesn't appear to define a type called _. 14 |
15 | 16 | 17 |
18 | 19 | 33 | 34 |
35 | 36 | 37 | 38 | 65 |
66 | 67 | 68 | 69 | 70 | 71 |
72 |
73 |
74 | -------------------------------------------------------------------------------- /docs/search.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | 4 | var node = (tag, attrs, children) => { 5 | var node = document.createElement(tag) 6 | for (var attr in attrs) { 7 | if (attr === 'style') { 8 | Object.assign(node.style, attrs[attr]) 9 | } else { 10 | node.setAttribute(attr, attrs[attr]) 11 | } 12 | } 13 | children && children.forEach(child => node.appendChild(typeof child === 'string' ? document.createTextNode(child) : child)) 14 | return node 15 | } 16 | var named = tag => (attrs, children) => node(tag, attrs, children) 17 | var div = named('div') 18 | var span = named('span') 19 | var a = named('a') 20 | var raw = text => { 21 | var node = document.createElement('div') 22 | node.innerHTML = text 23 | return node 24 | }; 25 | 26 | var render = (target, node) => { 27 | target.innerHTML = '' 28 | target.appendChild(node) 29 | }; 30 | 31 | 32 | var input = document.getElementById('search-input'); 33 | var index = elasticlunr.Index.load(window.searchindex); 34 | var config = {bool: 'AND', fields: {title: {boost: 2}, contents: {boost: 1}}, expand: true}; 35 | 36 | function escapeRegExp(string) { 37 | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 38 | } 39 | 40 | var walk = (node, fn) => { 41 | var nodes = [].slice.call(node.childNodes) 42 | nodes.forEach(child => { 43 | if (false === fn(child)) return; 44 | if (child.parentNode === node) { 45 | walk(child, fn) 46 | } 47 | }) 48 | } 49 | 50 | var highlightNode = (node, token) => { 51 | walk(node, node => { 52 | if (node.nodeName === '#text') { 53 | let at = 0; 54 | let pieces = []; 55 | node.textContent.replace(new RegExp(escapeRegExp(token), 'gi'), (matched, pos, full) => { 56 | pieces.push(document.createTextNode(full.slice(at, pos))) 57 | pieces.push(span({class: 'result-highlighted'}, [matched])) 58 | at = pos + matched.length 59 | }) 60 | if (pieces.length === 0) { 61 | return 62 | } 63 | if (at < node.textContent.length) { 64 | pieces.push(document.createTextNode(node.textContent.slice(at))) 65 | } 66 | node.replaceWith(...pieces) 67 | } 68 | }) 69 | } 70 | 71 | var highlightingNode = (text, tokens) => { 72 | var node = raw(text); 73 | tokens.forEach(token => highlightNode(node, token)) 74 | return node 75 | }; 76 | 77 | window.highlightNode = highlightNode 78 | 79 | var search = text => { 80 | var results = index.search(text, config).slice(0, 30); 81 | render(document.getElementById('search-results'), div( 82 | {}, 83 | results.map(({ref, score, doc: {href, title, contents, rendered, breadcrumb}}) => div( 84 | {class: 'result'}, 85 | [ 86 | div({style: {display: 'flex', justifyContent: 'space-between'}}, [ 87 | a({href, class: 'title'}, [title]), 88 | span({class: 'breadcrumb'}, [breadcrumb]) 89 | ]), 90 | div({}, [ 91 | highlightingNode(rendered, text.split(/\s+/g)) 92 | // raw(text.split(/\s+/g).reduce( 93 | // (text, item) => text.replace(new RegExp(escapeRegExp(item), 'ig'), "$&"), rendered 94 | // )) 95 | ]) 96 | ] 97 | )) 98 | )) 99 | } 100 | 101 | if (location.search.match(/^\?search=/)) { 102 | var query = location.search.slice('?search='.length) 103 | search(query) 104 | input.value = query 105 | } 106 | 107 | input.addEventListener('keyup', evt => { 108 | var text = evt.target.value 109 | window.history.replaceState({}, '', '?search=' + encodeURIComponent(text)) 110 | search(text) 111 | }) 112 | })(); 113 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: system-ui; 4 | font-weight: 200; 5 | margin: 48px auto; 6 | } 7 | 8 | body { 9 | font-size: 17px; 10 | line-height: 26px; 11 | font-weight: 400; 12 | letter-spacing: -0.021em; 13 | font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif; 14 | 15 | font-weight: 200; 16 | letter-spacing: 0.04em; 17 | } 18 | 19 | strong { 20 | font-weight: 400; 21 | } 22 | 23 | h4.item { 24 | font-family: FiraCode, sf mono, monospace; 25 | padding-top: 8px; 26 | border-top: 1px solid #ddd; 27 | white-space: pre; 28 | padding-bottom: 16px; 29 | margin-bottom: 0; 30 | margin-top: 16px; 31 | font-weight: 400; 32 | 33 | font-weight: 200; 34 | letter-spacing: 0.4em; 35 | } 36 | 37 | blockquote { 38 | border-left: 2px solid #006fc9; 39 | margin-left: 0; 40 | padding-left: 16px; 41 | } 42 | 43 | .code-block { 44 | margin: 32px 0; 45 | } 46 | 47 | pre { 48 | margin: 16px 0; 49 | padding: 8px 16px; 50 | background-color: #f1f8ff; 51 | overflow: auto; 52 | } 53 | 54 | pre.code-txt { 55 | border-radius: 3px; 56 | box-shadow: 0 0.5px 3px #aaa; 57 | background-color: white; 58 | line-height: 1; 59 | } 60 | 61 | div.compile-error { 62 | padding: 8px 16px; 63 | background-color: #ffebeb; 64 | margin-top: -24px; 65 | margin-bottom: 32px; 66 | white-space: pre-wrap; 67 | } 68 | 69 | .CodeMirror { 70 | height: auto!important; 71 | } 72 | 73 | .CodeMirror-error-mark { 74 | text-decoration-color: red; 75 | text-decoration-style: dashed; 76 | border-bottom: 1px dashed red; 77 | } 78 | 79 | .code-edit-run, 80 | .code-edit-button { 81 | position: absolute; 82 | top: 0; 83 | right: 0; 84 | padding: 4px 8px; 85 | background-color: #eee; 86 | border: none; 87 | cursor: pointer; 88 | opacity: 0; 89 | } 90 | 91 | .code-edit-run { 92 | opacity: 1; 93 | background-color: #8aff62; 94 | z-index: 1000; 95 | border-radius: 3px; 96 | } 97 | 98 | .code-block:hover .code-edit-button { 99 | opacity: 1; 100 | } 101 | 102 | .code-block-error { 103 | white-space: pre-wrap; 104 | padding: 4px 8px; 105 | background: white; 106 | border: 4px solid #f77; 107 | } 108 | 109 | .code-post, .code-pre { 110 | padding: 8px 16px; 111 | background-color: #eee; 112 | } 113 | 114 | .code-post { 115 | border-bottom-left-radius: 3px; 116 | border-bottom-right-radius: 3px; 117 | margin-bottom: 16px; 118 | margin-top: -16px; 119 | } 120 | 121 | .code-pre { 122 | border-top-left-radius: 3px; 123 | border-top-right-radius: 3px; 124 | } 125 | 126 | .code-post, 127 | .code-pre, 128 | .CodeMirror, 129 | .CodeMirror pre, 130 | .code-block, 131 | .code-block .CodeMirror pre, 132 | pre > code, 133 | h4.item, 134 | .type-viewer, 135 | div.compile-error, 136 | p code { 137 | font-family: FiraCode, 'SF Mono', Menlo, monospace; 138 | letter-spacing: 0; 139 | font-size: 15px; 140 | line-height: 1.2em; 141 | 142 | font-weight: 200; 143 | color: #585858; 144 | } 145 | 146 | .body { 147 | margin-left: 24px; 148 | margin-bottom: 48px; 149 | line-height: 1.5em; 150 | font-size: 20px; 151 | letter-spacing: 1px; 152 | } 153 | .body-empty, 154 | .include-body .body { 155 | margin-bottom: 0; 156 | } 157 | 158 | .include-body .item { 159 | padding: 0; 160 | border-top: 0; 161 | margin: 0; 162 | } 163 | 164 | .missing { 165 | font-style: italic; 166 | font-size: 16px; 167 | color: #777; 168 | } 169 | h1, h2 { 170 | margin-top: 24px; 171 | } 172 | h1, h2, h3, h4 { 173 | color: #444; 174 | } 175 | 176 | h4.module { 177 | font-size: 110%%; 178 | font-weight: 600; 179 | } 180 | .module-body { 181 | border-left: 1px solid #ddd; 182 | padding-left: 24px; 183 | } 184 | 185 | .body > pre:first-child { 186 | margin-top: 8px; 187 | } 188 | .body > p:first-child { 189 | margin-top: 0px; 190 | } 191 | 192 | .body > pre:last-child, 193 | .body > p:last-child { 194 | margin-bottom: 8px; 195 | } 196 | 197 | p code, h1 code, h2 code, h3 code, h4 code { 198 | padding: 1px 4px; 199 | background: #eee; 200 | border-radius: 3px; 201 | font-family: FiraCode, 'sf mono', monospace; 202 | font-size: 0.9em; 203 | background: white; 204 | /* color: #222; */ 205 | box-shadow: 0 0 1px #aaa; 206 | } 207 | 208 | .code-block > .CodeMirror { 209 | margin-bottom: 16px; 210 | } 211 | 212 | .block-log { 213 | font-family: FiraCode, 'sf mono', monospace; 214 | white-space: pre-wrap; 215 | padding: 8px 32px; 216 | margin-top: -16px; 217 | border-bottom-left-radius: 6px; 218 | border-bottom-right-radius: 6px; 219 | border: 1px solid #eee; 220 | } 221 | 222 | a { 223 | text-decoration: none; 224 | } 225 | a:hover, a:focus { 226 | text-decoration: underline; 227 | } 228 | 229 | .doc-item { 230 | font-size: 16px; 231 | } 232 | 233 | 234 | 235 | .container { 236 | display: flex; 237 | justify-content: center; 238 | margin: 48px auto; 239 | } 240 | 241 | .main { 242 | width: 800px; 243 | padding: 0 16px; 244 | box-sizing: border-box; 245 | position: relative; 246 | } 247 | 248 | .edit-link { 249 | position: absolute; 250 | top: 0; 251 | right: 16px; 252 | margin: 24px 0; 253 | } 254 | 255 | a, a:visited, 256 | .table-of-contents a.module, 257 | .project-listing a, 258 | .main a, .main a:visited { 259 | color: #025aa0; 260 | } 261 | 262 | .right-blank { 263 | width: 200px; 264 | } 265 | 266 | .sidebar div { 267 | flex-shrink: 0; 268 | } 269 | 270 | .sidebar { 271 | width: 200px; 272 | position: sticky; 273 | position: -webkit-sticky; 274 | top: 0; 275 | /* overflow: auto; */ 276 | max-height: 100vh; 277 | word-break: break-word; 278 | display: flex; 279 | flex-direction: column; 280 | padding-top: 16px; 281 | box-sizing: border-box; 282 | padding-left: 16px; 283 | } 284 | 285 | .sidebar .docs-listing { 286 | flex-shrink: 0; 287 | max-height: 30vh; 288 | overflow: auto; 289 | } 290 | .sidebar .docs-listing a { 291 | line-height: 1; 292 | padding: 6px 0; 293 | } 294 | .sidebar .table-of-contents { 295 | display: flex; 296 | flex-direction: column; 297 | padding: 8px; 298 | flex-shrink: 1; 299 | overflow: auto; 300 | } 301 | 302 | .sidebar .project-listing { 303 | display: flex; 304 | flex-direction: column; 305 | padding: 8px; 306 | font-size: 14px; 307 | padding-bottom: 32px; 308 | max-height: 30vh; 309 | flex-shrink: 0; 310 | overflow: auto; 311 | } 312 | 313 | .sidebar .toc-header, 314 | .sidebar .project-title { 315 | font-size: 14px; 316 | font-weight: bold; 317 | margin: 16px 0 8px; 318 | } 319 | 320 | .sidebar-expander { 321 | display: none; 322 | padding: 16px; 323 | } 324 | 325 | .docs-listing { 326 | display: flex; 327 | flex-direction: column; 328 | padding: 8px; 329 | } 330 | 331 | @media(max-width: 1000px) { 332 | .sidebar { 333 | position: static; 334 | width: 800px; 335 | margin: auto; 336 | padding: 0 16px 16px; 337 | max-height: unset; 338 | display: none; 339 | } 340 | .sidebar .project-listing { 341 | padding-bottom: 0px; 342 | } 343 | .sidebar-expander { 344 | text-align: center; 345 | width: 800px; 346 | margin: auto; 347 | display: block; 348 | } 349 | 350 | .sidebar.expanded { 351 | display: block; 352 | } 353 | 354 | .container { 355 | display: block; 356 | } 357 | 358 | .right-blank { 359 | display: none; 360 | } 361 | 362 | .main { 363 | margin: auto; 364 | } 365 | } 366 | 367 | @media(max-width: 620px) { 368 | .sidebar-expander, 369 | .sidebar { 370 | width: auto; 371 | margin: 0; 372 | } 373 | .main { 374 | width: auto; 375 | padding: 0; 376 | margin: 0; 377 | } 378 | .container { 379 | padding: 0 20px; 380 | } 381 | h4.item { 382 | font-size: 14px; 383 | overflow: auto; 384 | max-width: 100%%; 385 | padding-right: 16px; 386 | } 387 | .body { 388 | font-size: 17px; 389 | margin-left: 0; 390 | margin-bottom: 32px; 391 | } 392 | } 393 | 394 | .table-of-contents a { 395 | color: unset; 396 | padding: 2px 0; 397 | } 398 | .table-of-contents a:hover { 399 | background-color: #fafafa; 400 | } 401 | 402 | .table-of-contents a.header { 403 | font-weight: 400; 404 | } 405 | 406 | a.level-1 { 407 | margin-left: 0px; 408 | } 409 | 410 | a.level-2 { 411 | margin-left: 6px; 412 | } 413 | a.level-3 { 414 | margin-left: 12px; 415 | } 416 | a.level-4 { 417 | margin-left: 18px; 418 | } 419 | a.level-5 { 420 | margin-left: 24px; 421 | } 422 | 423 | #error-message { 424 | display: none; 425 | background-color: #fde6e6; 426 | padding: 8px 16px; 427 | border-radius: 4px; 428 | box-shadow: 0px 1px 3px #d8a2a2; 429 | margin-bottom: 32px; 430 | margin: 0 auto; 431 | max-width: 800px; 432 | } 433 | 434 | .external-link:after { 435 | /* from font-awesome */ 436 | background: url('data:image/svg+xml;utf8,'); 437 | margin-left: 4px; 438 | content: ' '; 439 | width: 10px; 440 | height: 10px; 441 | display: inline-block; 442 | background-size: contain; 443 | } 444 | 445 | pre.code > code { 446 | color: #515a63; 447 | } 448 | 449 | .code-block > .CodeMirror, 450 | pre.code { 451 | border-radius: 3px; 452 | box-shadow: 0 0.5px 3px #aaa; 453 | background-color: white; 454 | line-height: 1; 455 | } 456 | .code .ident, .code .pattern-ident { 457 | color: #000; 458 | font-weight: 300; 459 | } 460 | 461 | 462 | .code .module-identifier { 463 | color: #aa0; 464 | } 465 | 466 | .code .constructor, 467 | .code .pattern-constructor { 468 | color: #025e8c; 469 | font-weight: 400; 470 | } 471 | 472 | .code .type-value-identifier, 473 | .code .type-constructor, 474 | .code .type-module-identifier { 475 | color: #c100af; 476 | } 477 | 478 | .code .type-vbl, 479 | .code .open-module-identifier, 480 | .code .type-module-identifier, 481 | .code .let-module-identifier, 482 | .code .constructor-module-identifier, 483 | .code .switch-module-identifier, 484 | .code .record-module-identifier, 485 | .code .field-module-identifier 486 | { 487 | color: #a0a; 488 | } 489 | 490 | .code .field 491 | { 492 | color: #0aa; 493 | } 494 | 495 | .code .unused-identifier { 496 | color: #00a; 497 | } 498 | 499 | .code .declaration-var { 500 | color: #a50000; 501 | } 502 | 503 | .code .string, 504 | .code .int, 505 | .code .boolean, 506 | .code .float, 507 | .code .operator { 508 | font-weight: 300; 509 | } 510 | 511 | .code .string { 512 | color: #1a6700; 513 | } 514 | 515 | .code .int { 516 | color: #3030ff; 517 | } 518 | 519 | .code .boolean { 520 | color: #ff8f8f; 521 | } 522 | 523 | .code .float { 524 | color: #d49523; 525 | } 526 | 527 | .code .operator { 528 | color: #9b9bff; 529 | font-weight: bold; 530 | } 531 | 532 | .type-hovered { 533 | text-decoration: underline; 534 | } 535 | 536 | .type-viewer { 537 | position: absolute; 538 | display: none; 539 | background-color: white; 540 | white-space: pre; 541 | padding: 8px 16px; 542 | pointer-events: none; 543 | margin-top: 10px; 544 | margin-left: 10px; 545 | box-shadow: 0 1px 6px #555; 546 | border-radius: 3px; 547 | } 548 | 549 | 550 | .code-block { 551 | position: relative; 552 | } 553 | 554 | .block-target-large { 555 | height: 24px; 556 | background-color: #afa; 557 | cursor: pointer; 558 | text-align: center; 559 | } 560 | 561 | .block-target-right { 562 | background-color: #afa; 563 | cursor: pointer; 564 | height: 24px; 565 | background-color: #afa; 566 | border-radius: 3px; 567 | position: absolute; 568 | top: 0; 569 | left: 100%; 570 | margin-left: 16px; 571 | width: 32px; 572 | opacity: 1; 573 | display: flex; 574 | align-items: center; 575 | justify-content: center; 576 | } 577 | 578 | .block-target-small { 579 | cursor: pointer; 580 | height: 24px; 581 | background-color: #afa; 582 | border-radius: 3px; 583 | position: absolute; 584 | top: 0; 585 | right: 0; 586 | width: 32px; 587 | height: 2.5em; 588 | opacity: 0.1; 589 | transition: opacity 0.1s ease; 590 | display: flex; 591 | align-items: center; 592 | justify-content: center; 593 | } 594 | 595 | .code-block:hover .block-target-small { 596 | opacity: 1; 597 | } 598 | 599 | .block-target-container { 600 | position: absolute; 601 | left: 100%; 602 | top: 0; 603 | min-height: 42px; 604 | margin-left: 16px; 605 | padding: 8px 16px; 606 | border-radius: 3px; 607 | box-sizing: border-box; 608 | width: 200px; 609 | } 610 | .block-target-container.active { 611 | box-shadow: 0 0 1px #aaa inset; 612 | } 613 | 614 | 615 | .block-canvas-container { 616 | position: absolute; 617 | top: 0; 618 | left: 100%; 619 | box-shadow: 0 0 1px #aaa; 620 | border-radius: 3px; 621 | margin-left: 16px; 622 | } 623 | 624 | .block-canvas-play { 625 | position: absolute; 626 | top: 50%; 627 | left: 50%; 628 | font-size: 64px; 629 | /* margin-left: -26px; */ 630 | color: rgba(0, 0, 0, 0.2); 631 | /* margin-top: -17px; */ 632 | top: 0; 633 | left: 0; 634 | right: 0; 635 | bottom: 0; 636 | display: flex; 637 | justify-content: center; 638 | align-items: center; 639 | cursor: pointer; 640 | } 641 | 642 | @media(max-width: 1000px) { 643 | .block-target-container { 644 | position: static; 645 | margin-left: 0; 646 | margin-top: 8px; 647 | } 648 | .block-canvas-container { 649 | top: 0; 650 | left: 0; 651 | position: relative; 652 | margin-left: 0; 653 | margin-top: 8px; 654 | display: flex; 655 | justify-content: center; 656 | } 657 | } 658 | 659 | .parse-error { 660 | background: #ffd2d2; 661 | padding: 4px 8px; 662 | white-space: pre; 663 | margin-top: -16px; 664 | } 665 | .type-error { 666 | background: #ff9a9a; 667 | padding: 4px 8px; 668 | white-space: pre; 669 | margin-top: -16px; 670 | } 671 | 672 | @font-face{ 673 | font-family: 'FiraCode'; 674 | src: url('fonts/FiraCode-Light.woff2') format('woff2'); 675 | font-weight: 300; 676 | font-style: normal; 677 | } 678 | 679 | @font-face{ 680 | font-family: 'FiraCode'; 681 | src: url('fonts/FiraCode-Regular.woff2') format('woff2'); 682 | font-weight: 400; 683 | font-style: normal; 684 | } 685 | 686 | @font-face{ 687 | font-family: 'FiraCode'; 688 | src: url('fonts/FiraCode-Medium.woff2') format('woff2'); 689 | font-weight: 500; 690 | font-style: normal; 691 | } 692 | 693 | @font-face{ 694 | font-family: 'FiraCode'; 695 | src: url('fonts/FiraCode-Bold.woff2') format('woff2'); 696 | font-weight: 700; 697 | font-style: normal; 698 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astrada/bs-css-core", 3 | "version": "10.0.1", 4 | "description": "BuckleScript statically typed DSL for CSS", 5 | "main": "src/Css.re", 6 | "scripts": { 7 | "build": "bsb -make-world", 8 | "start": "bsb -make-world -w", 9 | "test": "yarn build && jest" 10 | }, 11 | "keywords": [ 12 | "bucklescript", 13 | "reason", 14 | "css" 15 | ], 16 | "author": "Andreas Møller ", 17 | "contributors": [ 18 | "Alessandro Strada " 19 | ], 20 | "license": "ISC", 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/astrada/bs-css-core.git" 24 | }, 25 | "devDependencies": { 26 | "@glennsl/bs-jest": "^0.4.9", 27 | "bs-platform": "^5.0.6" 28 | }, 29 | "jest": { 30 | "testURL": "http://localhost/" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Css_Colors.re: -------------------------------------------------------------------------------- 1 | let aliceblue = `hex("F0F8FF"); 2 | let antiquewhite = `hex("FAEBD7"); 3 | let aqua = `hex("00FFFF"); 4 | let aquamarine = `hex("7FFFD4"); 5 | let azure = `hex("F0FFFF"); 6 | let beige = `hex("F5F5DC"); 7 | let bisque = `hex("FFE4C4"); 8 | let black = `hex("000000"); 9 | let blanchedalmond = `hex("FFEBCD"); 10 | let blue = `hex("0000FF"); 11 | let blueviolet = `hex("8A2BE2"); 12 | let brown = `hex("A52A2A"); 13 | let burlywood = `hex("DEB887"); 14 | let cadetblue = `hex("5F9EA0"); 15 | let chartreuse = `hex("7FFF00"); 16 | let chocolate = `hex("D2691E"); 17 | let coral = `hex("FF7F50"); 18 | let cornflowerblue = `hex("6495ED"); 19 | let cornsilk = `hex("FFF8DC"); 20 | let crimson = `hex("DC143C"); 21 | let cyan = `hex("00FFFF"); 22 | let darkblue = `hex("00008B"); 23 | let darkcyan = `hex("008B8B"); 24 | let darkgoldenrod = `hex("B8860B"); 25 | let darkgray = `hex("A9A9A9"); 26 | let darkgreen = `hex("006400"); 27 | let darkgrey = `hex("A9A9A9"); 28 | let darkkhaki = `hex("BDB76B"); 29 | let darkmagenta = `hex("8B008B"); 30 | let darkolivegreen = `hex("556B2F"); 31 | let darkorange = `hex("FF8C00"); 32 | let darkorchid = `hex("9932CC"); 33 | let darkred = `hex("8B0000"); 34 | let darksalmon = `hex("E9967A"); 35 | let darkseagreen = `hex("8FBC8F"); 36 | let darkslateblue = `hex("483D8B"); 37 | let darkslategray = `hex("2F4F4F"); 38 | let darkslategrey = `hex("2F4F4F"); 39 | let darkturquoise = `hex("00CED1"); 40 | let darkviolet = `hex("9400D3"); 41 | let deeppink = `hex("FF1493"); 42 | let deepskyblue = `hex("00BFFF"); 43 | let dimgray = `hex("696969"); 44 | let dimgrey = `hex("696969"); 45 | let dodgerblue = `hex("1E90FF"); 46 | let firebrick = `hex("B22222"); 47 | let floralwhite = `hex("FFFAF0"); 48 | let forestgreen = `hex("228B22"); 49 | let fuchsia = `hex("FF00FF"); 50 | let gainsboro = `hex("DCDCDC"); 51 | let ghostwhite = `hex("F8F8FF"); 52 | let gold = `hex("FFD700"); 53 | let goldenrod = `hex("DAA520"); 54 | let gray = `hex("808080"); 55 | let green = `hex("008000"); 56 | let greenyellow = `hex("ADFF2F"); 57 | let grey = `hex("808080"); 58 | let honeydew = `hex("F0FFF0"); 59 | let hotpink = `hex("FF69B4"); 60 | let indianred = `hex("CD5C5C"); 61 | let indigo = `hex("4B0082"); 62 | let ivory = `hex("FFFFF0"); 63 | let khaki = `hex("F0E68C"); 64 | let lavender = `hex("E6E6FA"); 65 | let lavenderblush = `hex("FFF0F5"); 66 | let lawngreen = `hex("7CFC00"); 67 | let lemonchiffon = `hex("FFFACD"); 68 | let lightblue = `hex("ADD8E6"); 69 | let lightcoral = `hex("F08080"); 70 | let lightcyan = `hex("E0FFFF"); 71 | let lightgoldenrodyellow = `hex("FAFAD2"); 72 | let lightgray = `hex("D3D3D3"); 73 | let lightgreen = `hex("90EE90"); 74 | let lightgrey = `hex("D3D3D3"); 75 | let lightpink = `hex("FFB6C1"); 76 | let lightsalmon = `hex("FFA07A"); 77 | let lightseagreen = `hex("20B2AA"); 78 | let lightskyblue = `hex("87CEFA"); 79 | let lightslategray = `hex("778899"); 80 | let lightslategrey = `hex("778899"); 81 | let lightsteelblue = `hex("B0C4DE"); 82 | let lightyellow = `hex("FFFFE0"); 83 | let lime = `hex("00FF00"); 84 | let limegreen = `hex("32CD32"); 85 | let linen = `hex("FAF0E6"); 86 | let magenta = `hex("FF00FF"); 87 | let maroon = `hex("800000"); 88 | let mediumaquamarine = `hex("66CDAA"); 89 | let mediumblue = `hex("0000CD"); 90 | let mediumorchid = `hex("BA55D3"); 91 | let mediumpurple = `hex("9370DB"); 92 | let mediumseagreen = `hex("3CB371"); 93 | let mediumslateblue = `hex("7B68EE"); 94 | let mediumspringgreen = `hex("00FA9A"); 95 | let mediumturquoise = `hex("48D1CC"); 96 | let mediumvioletred = `hex("C71585"); 97 | let midnightblue = `hex("191970"); 98 | let mintcream = `hex("F5FFFA"); 99 | let mistyrose = `hex("FFE4E1"); 100 | let moccasin = `hex("FFE4B5"); 101 | let navajowhite = `hex("FFDEAD"); 102 | let navy = `hex("000080"); 103 | let oldlace = `hex("FDF5E6"); 104 | let olive = `hex("808000"); 105 | let olivedrab = `hex("6B8E23"); 106 | let orange = `hex("FFA500"); 107 | let orangered = `hex("FF4500"); 108 | let orchid = `hex("DA70D6"); 109 | let palegoldenrod = `hex("EEE8AA"); 110 | let palegreen = `hex("98FB98"); 111 | let paleturquoise = `hex("AFEEEE"); 112 | let palevioletred = `hex("DB7093"); 113 | let papayawhip = `hex("FFEFD5"); 114 | let peachpuff = `hex("FFDAB9"); 115 | let peru = `hex("CD853F"); 116 | let pink = `hex("FFC0CB"); 117 | let plum = `hex("DDA0DD"); 118 | let powderblue = `hex("B0E0E6"); 119 | let purple = `hex("800080"); 120 | let rebeccapurple = `hex("663399"); 121 | let red = `hex("FF0000"); 122 | let rosybrown = `hex("BC8F8F"); 123 | let royalblue = `hex("4169E1"); 124 | let saddlebrown = `hex("8B4513"); 125 | let salmon = `hex("FA8072"); 126 | let sandybrown = `hex("F4A460"); 127 | let seagreen = `hex("2E8B57"); 128 | let seashell = `hex("FFF5EE"); 129 | let sienna = `hex("A0522D"); 130 | let silver = `hex("C0C0C0"); 131 | let skyblue = `hex("87CEEB"); 132 | let slateblue = `hex("6A5ACD"); 133 | let slategray = `hex("708090"); 134 | let slategrey = `hex("708090"); 135 | let snow = `hex("FFFAFA"); 136 | let springgreen = `hex("00FF7F"); 137 | let steelblue = `hex("4682B4"); 138 | let tan = `hex("D2B48C"); 139 | let teal = `hex("008080"); 140 | let thistle = `hex("D8BFD8"); 141 | let tomato = `hex("FF6347"); 142 | let transparent = `transparent; 143 | let turquoise = `hex("40E0D0"); 144 | let violet = `hex("EE82EE"); 145 | let wheat = `hex("F5DEB3"); 146 | let white = `hex("FFFFFF"); 147 | let whitesmoke = `hex("F5F5F5"); 148 | let yellow = `hex("FFFF00"); 149 | let yellowgreen = `hex("9ACD3"); 150 | -------------------------------------------------------------------------------- /src/Css_Types.re: -------------------------------------------------------------------------------- 1 | module Cascading = { 2 | type t = [ | `initial | `inherit_ | `unset]; 3 | 4 | let initial = `initial; 5 | let inherit_ = `inherit_; 6 | let unset = `unset; 7 | 8 | let toString = 9 | fun 10 | | `initial => "initial" 11 | | `inherit_ => "inherit" 12 | | `unset => "unset"; 13 | }; 14 | 15 | module Time = { 16 | type t = [ | `s(float) | `ms(float)]; 17 | 18 | let s = x => `s(x); 19 | let ms = x => `ms(x); 20 | 21 | let toString = 22 | fun 23 | | `s(v) => Js.Float.toString(v) ++ "s" 24 | | `ms(v) => Js.Float.toString(v) ++ "ms"; 25 | }; 26 | 27 | module Percentage = { 28 | type t = [ | `percent(float)]; 29 | 30 | let pct = x => `percent(x); 31 | 32 | let toString = 33 | fun 34 | | `percent(x) => Js.Float.toString(x) ++ "%"; 35 | }; 36 | 37 | module Url = { 38 | type t = [ | `url(string)]; 39 | 40 | let toString = 41 | fun 42 | | `url(s) => "url(" ++ s ++ ")"; 43 | }; 44 | 45 | module Length = { 46 | type t = [ 47 | | `ch(float) 48 | | `em(float) 49 | | `ex(float) 50 | | `rem(float) 51 | | `vh(float) 52 | | `vw(float) 53 | | `vmin(float) 54 | | `vmax(float) 55 | | `px(int) 56 | | `pxFloat(float) 57 | | `cm(float) 58 | | `mm(float) 59 | | `inch(float) 60 | | `pc(float) 61 | | `pt(int) 62 | | `zero 63 | | `calc([ | `add | `sub], t, t) 64 | | `percent(float) 65 | ]; 66 | 67 | let ch = x => `ch(x); 68 | let em = x => `em(x); 69 | let ex = x => `ex(x); 70 | let rem = x => `rem(x); 71 | let vh = x => `vh(x); 72 | let vw = x => `vw(x); 73 | let vmin = x => `vmin(x); 74 | let vmax = x => `vmax(x); 75 | let px = x => `px(x); 76 | let pxFloat = x => `pxFloat(x); 77 | let cm = x => `cm(x); 78 | let mm = x => `mm(x); 79 | let inch = x => `inch(x); 80 | let pc = x => `pc(x); 81 | let pt = x => `pt(x); 82 | let zero = `zero; 83 | 84 | let rec toString = 85 | fun 86 | | `ch(x) => Js.Float.toString(x) ++ "ch" 87 | | `em(x) => Js.Float.toString(x) ++ "em" 88 | | `ex(x) => Js.Float.toString(x) ++ "ex" 89 | | `rem(x) => Js.Float.toString(x) ++ "rem" 90 | | `vh(x) => Js.Float.toString(x) ++ "vh" 91 | | `vw(x) => Js.Float.toString(x) ++ "vw" 92 | | `vmin(x) => Js.Float.toString(x) ++ "vmin" 93 | | `vmax(x) => Js.Float.toString(x) ++ "vmax" 94 | | `px(x) => Js.Int.toString(x) ++ "px" 95 | | `pxFloat(x) => Js.Float.toString(x) ++ "px" 96 | | `cm(x) => Js.Float.toString(x) ++ "cm" 97 | | `mm(x) => Js.Float.toString(x) ++ "mm" 98 | | `inch(x) => Js.Float.toString(x) ++ "in" 99 | | `pc(x) => Js.Float.toString(x) ++ "pc" 100 | | `pt(x) => Js.Int.toString(x) ++ "pt" 101 | | `zero => "0" 102 | 103 | | `calc(`add, a, b) => 104 | "calc(" ++ toString(a) ++ " + " ++ toString(b) ++ ")" 105 | | `calc(`sub, a, b) => 106 | "calc(" ++ toString(a) ++ " - " ++ toString(b) ++ ")" 107 | | `percent(x) => Js.Float.toString(x) ++ "%"; 108 | }; 109 | 110 | module Angle = { 111 | type t = [ | `deg(float) | `rad(float) | `grad(float) | `turn(float)]; 112 | 113 | let deg = (x: float) => `deg(x); 114 | let rad = (x: float) => `rad(x); 115 | let grad = (x: float) => `grad(x); 116 | let turn = (x: float) => `turn(x); 117 | 118 | let toString = 119 | fun 120 | | `deg(x) => Js.Float.toString(x) ++ "deg" 121 | | `rad(x) => Js.Float.toString(x) ++ "rad" 122 | | `grad(x) => Js.Float.toString(x) ++ "grad" 123 | | `turn(x) => Js.Float.toString(x) ++ "turn"; 124 | }; 125 | 126 | module Direction = { 127 | type t = [ | `ltr | `rtl]; 128 | 129 | let ltr = `ltr; 130 | let rtl = `rtl; 131 | 132 | let toString = 133 | fun 134 | | `ltr => "ltr" 135 | | `rtl => "rtl"; 136 | }; 137 | 138 | module Position = { 139 | type t = [ | `absolute | `relative | `static | `fixed | `sticky]; 140 | 141 | let absolute = `absolute; 142 | let relative = `relative; 143 | let static = `static; 144 | let fixed = `fixed; 145 | let sticky = `sticky; 146 | 147 | let toString = 148 | fun 149 | | `absolute => "absolute" 150 | | `relative => "relative" 151 | | `static => "static" 152 | | `fixed => "fixed" 153 | | `sticky => "sticky"; 154 | }; 155 | 156 | module Resize = { 157 | type t = [ | `none | `both | `horizontal | `vertical | `block | `inline]; 158 | 159 | let none = `none; 160 | let both = `both; 161 | let horizontal = `horizontal; 162 | let vertical = `vertical; 163 | let block = `block; 164 | let inline = `inline; 165 | 166 | let toString = 167 | fun 168 | | `none => "none" 169 | | `both => "both" 170 | | `horizontal => "horizontal" 171 | | `vertical => "vertical" 172 | | `block => "block" 173 | | `inline => "inline"; 174 | }; 175 | 176 | module FontVariant = { 177 | type t = [ | `normal | `smallCaps]; 178 | 179 | let normal = `normal; 180 | let smallCaps = `smallCaps; 181 | 182 | let toString = 183 | fun 184 | | `normal => "normal" 185 | | `smallCaps => "smallCaps"; 186 | }; 187 | 188 | module FontStyle = { 189 | type t = [ | `normal | `italic | `oblique]; 190 | 191 | let normal = `normal; 192 | let italic = `italic; 193 | let oblique = `oblique; 194 | 195 | let toString = 196 | fun 197 | | `normal => "normal" 198 | | `italic => "italic" 199 | | `oblique => "oblique"; 200 | }; 201 | 202 | module FlexBasis = { 203 | type t = [ 204 | | `auto 205 | | `fill 206 | | `content 207 | | `maxContent 208 | | `minContent 209 | | `fitContent 210 | ]; 211 | 212 | let fill = `fill; 213 | let content = `content; 214 | let maxContent = `maxContent; 215 | let minContent = `minContent; 216 | let fitContent = `fitContent; 217 | 218 | let toString = 219 | fun 220 | | `auto => "auto" 221 | | `fill => "fill" 222 | | `content => "content" 223 | | `maxContent => "max-content" 224 | | `minContent => "min-content" 225 | | `fitContent => "fit-content"; 226 | }; 227 | 228 | module Overflow = { 229 | type t = [ | `hidden | `visible | `scroll | `auto]; 230 | 231 | let hidden = `hidden; 232 | let visible = `visible; 233 | let scroll = `scroll; 234 | let auto = `auto; 235 | 236 | let toString = 237 | fun 238 | | `hidden => "hidden" 239 | | `visible => "visible" 240 | | `scroll => "scroll" 241 | | `auto => "auto"; 242 | }; 243 | 244 | module Margin = { 245 | type t = [ | `auto]; 246 | 247 | let auto = `auto; 248 | 249 | let toString = 250 | fun 251 | | `auto => "auto"; 252 | }; 253 | 254 | module GridAutoFlow = { 255 | type t = [ | `column | `row | `columnDense | `rowDense]; 256 | 257 | let toString = 258 | fun 259 | | `column => "column" 260 | | `row => "row" 261 | | `columnDense => "column dense" 262 | | `rowDense => "row dense"; 263 | }; 264 | 265 | module GridColumnGap = { 266 | type t = [ | `normal]; 267 | 268 | let toString = 269 | fun 270 | | `normal => "normal"; 271 | }; 272 | 273 | module VerticalAlign = { 274 | type t = [ 275 | | `baseline 276 | | `sub 277 | | `super 278 | | `top 279 | | `textTop 280 | | `middle 281 | | `bottom 282 | | `textBottom 283 | ]; 284 | 285 | let toString = 286 | fun 287 | | `baseline => "baseline" 288 | | `sub => "sub" 289 | | `super => "super" 290 | | `top => "top" 291 | | `textTop => "text-top" 292 | | `middle => "middle" 293 | | `bottom => "bottom" 294 | | `textBottom => "text-bottom"; 295 | }; 296 | 297 | module TimingFunction = { 298 | type t = [ 299 | | `linear 300 | | `ease 301 | | `easeIn 302 | | `easeOut 303 | | `easeInOut 304 | | `stepStart 305 | | `stepEnd 306 | | `steps(int, [ | `start | `end_]) 307 | | `cubicBezier(float, float, float, float) 308 | ]; 309 | 310 | let toString = 311 | fun 312 | | `linear => "linear" 313 | | `ease => "ease" 314 | | `easeIn => "ease-out" 315 | | `easeOut => "ease-out" 316 | | `easeInOut => "ease-in-out" 317 | | `stepStart => "step-start" 318 | | `stepEnd => "step-end" 319 | | `steps(i, `start) => "steps(" ++ Js.Int.toString(i) ++ ", start)" 320 | | `steps(i, `end_) => "steps(" ++ Js.Int.toString(i) ++ ", end)" 321 | | `cubicBezier(a, b, c, d) => 322 | "cubic-bezier(" 323 | ++ Js.Float.toString(a) 324 | ++ ", " 325 | ++ Js.Float.toString(b) 326 | ++ ", " 327 | ++ Js.Float.toString(c) 328 | ++ ", " 329 | ++ Js.Float.toString(d) 330 | ++ ")"; 331 | }; 332 | 333 | module RepeatValue = { 334 | type t = [ | `autoFill | `autoFit | `num(int)]; 335 | 336 | let toString = 337 | fun 338 | | `autoFill => "auto-fill" 339 | | `autoFit => "auto-fit" 340 | | `num(x) => Js.Int.toString(x); 341 | }; 342 | 343 | module ListStyleType = { 344 | type t = [ 345 | | `disc 346 | | `circle 347 | | `square 348 | | `decimal 349 | | `lowerAlpha 350 | | `upperAlpha 351 | | `lowerGreek 352 | | `lowerLatin 353 | | `upperLatin 354 | | `lowerRoman 355 | | `upperRoman 356 | | `none 357 | ]; 358 | 359 | let toString = 360 | fun 361 | | `disc => "disc" 362 | | `circle => "circle" 363 | | `square => "square" 364 | | `decimal => "decimal" 365 | | `lowerAlpha => "lower-alpha" 366 | | `upperAlpha => "upper-alpha" 367 | | `lowerGreek => "lower-greek" 368 | | `lowerLatin => "lower-latin" 369 | | `upperLatin => "upper-latin" 370 | | `lowerRoman => "lower-roman" 371 | | `upperRoman => "upper-roman" 372 | | `none => "none"; 373 | }; 374 | 375 | module ListStylePosition = { 376 | type t = [ | `inside | `outside]; 377 | 378 | let toString = 379 | fun 380 | | `inside => "inside" 381 | | `outside => "outside"; 382 | }; 383 | 384 | module OutlineStyle = { 385 | type t = [ 386 | | `none 387 | | `hidden 388 | | `dotted 389 | | `dashed 390 | | `solid 391 | | `double 392 | | `groove 393 | | `ridge 394 | | `inset 395 | | `outset 396 | ]; 397 | 398 | let toString = 399 | fun 400 | | `none => "none" 401 | | `hidden => "hidden" 402 | | `dotted => "dotted" 403 | | `dashed => "dashed" 404 | | `solid => "solid" 405 | | `double => "double" 406 | | `groove => "grove" 407 | | `ridge => "ridge" 408 | | `inset => "inset" 409 | | `outset => "outset"; 410 | }; 411 | 412 | module FontWeight = { 413 | type t = [ 414 | | `num(int) 415 | | `thin 416 | | `extraLight 417 | | `light 418 | | `normal 419 | | `medium 420 | | `semiBold 421 | | `bold 422 | | `extraBold 423 | | `black 424 | | `lighter 425 | | `bolder 426 | ]; 427 | 428 | let toString = x => 429 | switch (x) { 430 | | `num(n) => Js.Int.toString(n) 431 | | `thin => "100" 432 | | `extraLight => "200" 433 | | `light => "300" 434 | | `normal => "400" 435 | | `medium => "500" 436 | | `semiBold => "600" 437 | | `bold => "700" 438 | | `extraBold => "800" 439 | | `black => "900" 440 | | `lighter => "lighter" 441 | | `bolder => "bolder" 442 | }; 443 | }; 444 | 445 | module Transform = { 446 | type t = [ 447 | | `translate(Length.t, Length.t) 448 | | `translate3d(Length.t, Length.t, Length.t) 449 | | `translateX(Length.t) 450 | | `translateY(Length.t) 451 | | `translateZ(Length.t) 452 | | `scale(float, float) 453 | | `scale3d(float, float, float) 454 | | `scaleX(float) 455 | | `scaleY(float) 456 | | `scaleZ(float) 457 | | `rotate(Angle.t) 458 | | `rotate3d(float, float, float, Angle.t) 459 | | `rotateX(Angle.t) 460 | | `rotateY(Angle.t) 461 | | `rotateZ(Angle.t) 462 | | `skew(Angle.t, Angle.t) 463 | | `skewX(Angle.t) 464 | | `skewY(Angle.t) 465 | | `perspective(int) 466 | ]; 467 | 468 | let string_of_scale = (x, y) => 469 | "scale(" ++ Js.Float.toString(x) ++ ", " ++ Js.Float.toString(y) ++ ")"; 470 | 471 | let string_of_translate3d = (x, y, z) => 472 | "translate3d(" 473 | ++ Length.toString(x) 474 | ++ ", " 475 | ++ Length.toString(y) 476 | ++ ", " 477 | ++ Length.toString(z) 478 | ++ ")"; 479 | 480 | let toString = 481 | fun 482 | | `translate(x, y) => 483 | "translate(" ++ Length.toString(x) ++ ", " ++ Length.toString(y) ++ ")" 484 | | `translate3d(x, y, z) => string_of_translate3d(x, y, z) 485 | | `translateX(x) => "translateX(" ++ Length.toString(x) ++ ")" 486 | | `translateY(y) => "translateY(" ++ Length.toString(y) ++ ")" 487 | | `translateZ(z) => "translateZ(" ++ Length.toString(z) ++ ")" 488 | | `scale(x, y) => string_of_scale(x, y) 489 | | `scale3d(x, y, z) => 490 | "scale3d(" 491 | ++ Js.Float.toString(x) 492 | ++ ", " 493 | ++ Js.Float.toString(y) 494 | ++ ", " 495 | ++ Js.Float.toString(z) 496 | ++ ")" 497 | | `scaleX(x) => "scaleX(" ++ Js.Float.toString(x) ++ ")" 498 | | `scaleY(y) => "scaleY(" ++ Js.Float.toString(y) ++ ")" 499 | | `scaleZ(z) => "scaleZ(" ++ Js.Float.toString(z) ++ ")" 500 | | `rotate(a) => "rotate(" ++ Angle.toString(a) ++ ")" 501 | | `rotate3d(x, y, z, a) => 502 | "rotate3d(" 503 | ++ Js.Float.toString(x) 504 | ++ ", " 505 | ++ Js.Float.toString(y) 506 | ++ ", " 507 | ++ Js.Float.toString(z) 508 | ++ ", " 509 | ++ Angle.toString(a) 510 | ++ ")" 511 | | `rotateX(a) => "rotateX(" ++ Angle.toString(a) ++ ")" 512 | | `rotateY(a) => "rotateY(" ++ Angle.toString(a) ++ ")" 513 | | `rotateZ(a) => "rotateZ(" ++ Angle.toString(a) ++ ")" 514 | | `skew(x, y) => 515 | "skew(" ++ Angle.toString(x) ++ ", " ++ Angle.toString(y) ++ ")" 516 | | `skewX(a) => "skewX(" ++ Angle.toString(a) ++ ")" 517 | | `skewY(a) => "skewY(" ++ Angle.toString(a) ++ ")" 518 | | `perspective(x) => "perspective(" ++ Js.Int.toString(x) ++ ")"; 519 | }; 520 | 521 | module AnimationDirection = { 522 | type t = [ | `normal | `reverse | `alternate | `alternateReverse]; 523 | 524 | let toString = 525 | fun 526 | | `normal => "normal" 527 | | `reverse => "reverse" 528 | | `alternate => "alternate" 529 | | `alternateReverse => "alternate-reverse"; 530 | }; 531 | 532 | module AnimationFillMode = { 533 | type t = [ | `none | `forwards | `backwards | `both]; 534 | 535 | let toString = 536 | fun 537 | | `none => "none" 538 | | `forwards => "forwards" 539 | | `backwards => "backwards" 540 | | `both => "both"; 541 | }; 542 | 543 | module AnimationIterationCount = { 544 | type t = [ | `infinite | `count(int)]; 545 | 546 | let toString = 547 | fun 548 | | `infinite => "infinite" 549 | | `count(x) => Js.Int.toString(x); 550 | }; 551 | 552 | module AnimationPlayState = { 553 | type t = [ | `paused | `running]; 554 | 555 | let toString = 556 | fun 557 | | `paused => "paused" 558 | | `running => "running"; 559 | }; 560 | 561 | module Cursor = { 562 | type t = [ 563 | | `auto 564 | | `default 565 | | `none 566 | | `contextMenu 567 | | `help 568 | | `pointer 569 | | `progress 570 | | `wait 571 | | `cell 572 | | `crosshair 573 | | `text 574 | | `verticalText 575 | | `alias 576 | | `copy 577 | | `move 578 | | `noDrop 579 | | `notAllowed 580 | | `grab 581 | | `grabbing 582 | | `allScroll 583 | | `colResize 584 | | `rowResize 585 | | `nResize 586 | | `eResize 587 | | `sResize 588 | | `wResize 589 | | `neResize 590 | | `nwResize 591 | | `seResize 592 | | `swResize 593 | | `ewResize 594 | | `nsResize 595 | | `neswResize 596 | | `nwseResize 597 | | `zoomIn 598 | | `zoomOut 599 | ]; 600 | 601 | let toString = x => 602 | switch (x) { 603 | | `auto => "auto" 604 | | `default => "default" 605 | | `none => "none" 606 | | `contextMenu => "context-menu" 607 | | `help => "help" 608 | | `pointer => "pointer" 609 | | `progress => "progress" 610 | | `wait => "wait" 611 | | `cell => "cell" 612 | | `crosshair => "crosshair" 613 | | `text => "text" 614 | | `verticalText => "vertical-text" 615 | | `alias => "alias" 616 | | `copy => "copy" 617 | | `move => "move" 618 | | `noDrop => "no-drop" 619 | | `notAllowed => "not-allowed" 620 | | `grab => "grab" 621 | | `grabbing => "grabbing" 622 | | `allScroll => "all-scroll" 623 | | `colResize => "col-resize" 624 | | `rowResize => "row-resize" 625 | | `nResize => "n-resize" 626 | | `eResize => "e-resize" 627 | | `sResize => "s-resize" 628 | | `wResize => "w-resize" 629 | | `neResize => "ne-resize" 630 | | `nwResize => "nw-resize" 631 | | `seResize => "se-resize" 632 | | `swResize => "sw-resize" 633 | | `ewResize => "ew-resize" 634 | | `nsResize => "ns-resize" 635 | | `neswResize => "nesw-resize" 636 | | `nwseResize => "nwse-resize" 637 | | `zoomIn => "zoom-in" 638 | | `zoomOut => "zoom-out" 639 | }; 640 | }; 641 | 642 | module Color = { 643 | type t = [ 644 | | `rgb(int, int, int) 645 | | `rgba(int, int, int, float) 646 | | `hsl(Angle.t, [ | `percent(float)], [ | `percent(float)]) 647 | | `hsla( 648 | Angle.t, 649 | [ | `percent(float)], 650 | [ | `percent(float)], 651 | [ | `num(float) | `percent(float)], 652 | ) 653 | | `hex(string) 654 | | `transparent 655 | | `currentColor 656 | ]; 657 | 658 | let rgb = (r, g, b) => `rgb((r, g, b)); 659 | let rgba = (r, g, b, a) => `rgba((r, g, b, a)); 660 | let hsl = (h, s, l) => `hsl((h, `percent(s), `percent(l))); 661 | let hsla = (h, s, l, a) => `hsla((h, `percent(s), `percent(l), a)); 662 | let hex = x => `hex(x); 663 | let transparent = `transparent; 664 | let currentColor = `currentColor; 665 | 666 | let string_of_alpha = 667 | fun 668 | | `num(f) => Js.Float.toString(f) 669 | | `percent(p) => Js.Float.toString(p) ++ "%"; 670 | 671 | let string_of_percent = 672 | fun 673 | | `percent(x) => Js.Float.toString(x) ++ "%"; 674 | 675 | let toString = 676 | fun 677 | | `rgb(r, g, b) => 678 | "rgb(" 679 | ++ Js.Int.toString(r) 680 | ++ ", " 681 | ++ Js.Int.toString(g) 682 | ++ ", " 683 | ++ Js.Int.toString(b) 684 | ++ ")" 685 | | `rgba(r, g, b, a) => 686 | "rgba(" 687 | ++ Js.Int.toString(r) 688 | ++ ", " 689 | ++ Js.Int.toString(g) 690 | ++ ", " 691 | ++ Js.Int.toString(b) 692 | ++ ", " 693 | ++ Js.Float.toString(a) 694 | ++ ")" 695 | | `hsl(h, s, l) => 696 | "hsl(" 697 | ++ Angle.toString(h) 698 | ++ ", " 699 | ++ string_of_percent(s) 700 | ++ ", " 701 | ++ string_of_percent(l) 702 | ++ ")" 703 | | `hsla(h, s, l, a) => 704 | "hsla(" 705 | ++ Angle.toString(h) 706 | ++ ", " 707 | ++ string_of_percent(s) 708 | ++ ", " 709 | ++ string_of_percent(l) 710 | ++ ", " 711 | ++ string_of_alpha(a) 712 | ++ ")" 713 | | `hex(s) => "#" ++ s 714 | | `transparent => "transparent" 715 | | `currentColor => "currentColor"; 716 | }; 717 | 718 | module BorderStyle = { 719 | type t = [ 720 | | `none 721 | | `hidden 722 | | `dotted 723 | | `dashed 724 | | `solid 725 | | `double 726 | | `groove 727 | | `ridge 728 | | `inset 729 | | `outset 730 | ]; 731 | 732 | let toString = 733 | fun 734 | | `none => "none" 735 | | `hidden => "hidden" 736 | | `dotted => "dotted" 737 | | `dashed => "dashed" 738 | | `solid => "solid" 739 | | `double => "double" 740 | | `groove => "groove" 741 | | `ridge => "ridge" 742 | | `inset => "inset" 743 | | `outset => "outset"; 744 | }; 745 | 746 | module PointerEvents = { 747 | type t = [ | `auto | `none]; 748 | 749 | let toString = 750 | fun 751 | | `auto => "auto" 752 | | `none => "none"; 753 | }; 754 | 755 | module Perspective = { 756 | type t = [ | `none]; 757 | 758 | let toString = 759 | fun 760 | | `none => "none"; 761 | }; 762 | 763 | module LetterSpacing = { 764 | type t = [ | `normal]; 765 | 766 | let normal = `normal; 767 | 768 | let toString = 769 | fun 770 | | `normal => "normal"; 771 | }; 772 | 773 | module LineHeight = { 774 | type t = [ | `normal | `abs(float)]; 775 | 776 | let toString = 777 | fun 778 | | `normal => "normal" 779 | | `abs(x) => Js.Float.toString(x); 780 | }; 781 | 782 | module WordSpacing = { 783 | type t = [ | `normal]; 784 | 785 | let toString = 786 | fun 787 | | `normal => "normal"; 788 | }; 789 | 790 | module DisplayOutside = { 791 | type t = [ | `block | `inline | `runIn]; 792 | 793 | let toString = 794 | fun 795 | | `block => "block" 796 | | `inline => "inline" 797 | | `runIn => "run-in"; 798 | }; 799 | 800 | module DisplayInside = { 801 | type t = [ | `table | `flex | `grid]; 802 | 803 | let toString = 804 | fun 805 | | `table => "table" 806 | | `flex => "flex" 807 | | `grid => "grid"; 808 | }; 809 | 810 | module DisplayListItem = { 811 | type t = [ | `listItem]; 812 | 813 | let toString = 814 | fun 815 | | `listItem => "list-item"; 816 | }; 817 | 818 | module DisplayInternal = { 819 | type t = [ 820 | | `tableRowGroup 821 | | `tableHeaderGroup 822 | | `tableFooterGroup 823 | | `tableRow 824 | | `tableCell 825 | | `tableColumnGroup 826 | | `tableColumn 827 | | `tableCaption 828 | ]; 829 | 830 | let toString = 831 | fun 832 | | `tableRowGroup => "table-row-group" 833 | | `tableHeaderGroup => "table-header-group" 834 | | `tableFooterGroup => "table-footer-group" 835 | | `tableRow => "table-row" 836 | | `tableCell => "table-cell" 837 | | `tableColumnGroup => "table-column-group" 838 | | `tableColumn => "table-column" 839 | | `tableCaption => "table-caption"; 840 | }; 841 | 842 | module DisplayBox = { 843 | type t = [ | `contents | `none]; 844 | 845 | let toString = 846 | fun 847 | | `contents => "contents" 848 | | `none => "none"; 849 | }; 850 | 851 | module DisplayLegacy = { 852 | type t = [ | `inlineBlock | `inlineFlex | `inlineGrid | `inlineTable]; 853 | 854 | let toString = 855 | fun 856 | | `inlineBlock => "inline-block" 857 | | `inlineFlex => "inline-flex" 858 | | `inlineGrid => "inline-grid" 859 | | `inlineTable => "inline-table"; 860 | }; 861 | 862 | module JustifySelf = { 863 | type t = [ | `auto | `normal | `stretch]; 864 | 865 | let toString = 866 | fun 867 | | `auto => "auto" 868 | | `normal => "normal" 869 | | `stretch => "stretch"; 870 | }; 871 | 872 | module PositionalAlignment = { 873 | type t = [ 874 | | `center 875 | | `start 876 | | `end_ 877 | | `flexStart 878 | | `flexEnd 879 | | `selfStart 880 | | `selfEnd 881 | | `left 882 | | `right 883 | ]; 884 | 885 | let toString = 886 | fun 887 | | `center => "center" 888 | | `start => "start" 889 | | `end_ => "end" 890 | | `flexStart => "flex-start" 891 | | `flexEnd => "flex-end" 892 | | `selfStart => "self-start" 893 | | `selfEnd => "self-end" 894 | | `left => "left" 895 | | `right => "right"; 896 | }; 897 | 898 | module BaselineAlignment = { 899 | type t = [ | `baseline]; 900 | 901 | let toString = 902 | fun 903 | | `baseline => "baseline"; 904 | }; 905 | 906 | module NormalAlignment = { 907 | type t = [ | `normal]; 908 | 909 | let toString = 910 | fun 911 | | `normal => "normal"; 912 | }; 913 | 914 | module DistributedAlignment = { 915 | type t = [ | `spaceBetween | `spaceAround | `spaceEvenly | `stretch]; 916 | 917 | let toString = 918 | fun 919 | | `spaceBetween => "space-between" 920 | | `spaceAround => "space-around" 921 | | `spaceEvenly => "space-evenly" 922 | | `stretch => "stretch"; 923 | }; 924 | 925 | module TextAlign = { 926 | type t = [ | `left | `right | `center | `justify]; 927 | 928 | let toString = 929 | fun 930 | | `left => "left" 931 | | `right => "right" 932 | | `center => "center" 933 | | `justify => "justify"; 934 | }; 935 | 936 | module WordBreak = { 937 | type t = [ | `normal | `breakAll | `keepAll]; 938 | 939 | let toString = 940 | fun 941 | | `normal => "normal" 942 | | `breakAll => "break-all" 943 | | `keepAll => "keep-all"; 944 | }; 945 | 946 | module WhiteSpace = { 947 | type t = [ | `normal | `nowrap | `pre | `preLine | `preWrap | `breakSpaces]; 948 | 949 | let toString = 950 | fun 951 | | `normal => "normal" 952 | | `nowrap => "nowrap" 953 | | `pre => "pre" 954 | | `preLine => "pre-line" 955 | | `preWrap => "pre-wrap" 956 | | `breakSpaces => "break-spaces"; 957 | }; 958 | 959 | module AlignItems = { 960 | type t = [ | `normal | `stretch]; 961 | 962 | let toString = 963 | fun 964 | | `normal => "normal" 965 | | `stretch => "stretch"; 966 | }; 967 | 968 | module AlignSelf = { 969 | type t = [ | `auto | `normal]; 970 | 971 | let toString = 972 | fun 973 | | `auto => "auto" 974 | | `normal => "normal"; 975 | }; 976 | 977 | module AlignContent = { 978 | type t = [ | `center | `start | `end_ | `flexStart | `flexEnd]; 979 | 980 | let toString = 981 | fun 982 | | `center => "center" 983 | | `start => "start" 984 | | `end_ => "end" 985 | | `flexStart => "flex-start" 986 | | `flexEnd => "flex-end"; 987 | }; 988 | 989 | module ObjectFit = { 990 | type t = [ | `fill | `contain | `cover | `none | `scaleDown]; 991 | 992 | let toString = 993 | fun 994 | | `fill => "fill" 995 | | `contain => "contain" 996 | | `cover => "cover" 997 | | `none => "none" 998 | | `scaleDown => "scale-down"; 999 | }; 1000 | 1001 | module Clear = { 1002 | type t = [ | `none | `left | `right | `both | `inlineStart | `inlineEnd]; 1003 | 1004 | let toString = 1005 | fun 1006 | | `none => "none" 1007 | | `left => "left" 1008 | | `right => "right" 1009 | | `both => "both" 1010 | | `inlineStart => "inline-start" 1011 | | `inlineEnd => "inline-end"; 1012 | }; 1013 | 1014 | module Float = { 1015 | type t = [ | `left | `right | `none | `inlineStart | `inlineEnd]; 1016 | 1017 | let toString = 1018 | fun 1019 | | `left => "left" 1020 | | `right => "right" 1021 | | `none => "none" 1022 | | `inlineStart => "inline-start" 1023 | | `inlineEnd => "inline-end"; 1024 | }; 1025 | 1026 | module Visibility = { 1027 | type t = [ | `visible | `hidden | `collapse]; 1028 | 1029 | let toString = 1030 | fun 1031 | | `visible => "visible" 1032 | | `hidden => "hidden" 1033 | | `collapse => "collapse"; 1034 | }; 1035 | 1036 | module TableLayout = { 1037 | type t = [ | `auto | `fixed]; 1038 | 1039 | let toString = 1040 | fun 1041 | | `auto => "auto" 1042 | | `fixed => "fixed"; 1043 | }; 1044 | 1045 | module BorderCollapse = { 1046 | type t = [ | `collapse | `separate]; 1047 | 1048 | let toString = 1049 | fun 1050 | | `collapse => "collapse" 1051 | | `separate => "separate"; 1052 | }; 1053 | 1054 | module FlexWrap = { 1055 | type t = [ | `nowrap | `wrap | `wrapReverse]; 1056 | 1057 | let toString = 1058 | fun 1059 | | `nowrap => "nowrap" 1060 | | `wrap => "wrap" 1061 | | `wrapReverse => "wrap-reverse"; 1062 | }; 1063 | 1064 | module FlexDirection = { 1065 | type t = [ | `row | `rowReverse | `column | `columnReverse]; 1066 | 1067 | let toString = 1068 | fun 1069 | | `row => "row" 1070 | | `rowReverse => "row-reverse" 1071 | | `column => "column" 1072 | | `columnReverse => "column-reverse"; 1073 | }; 1074 | 1075 | module BoxSizing = { 1076 | type t = [ | `contentBox | `borderBox]; 1077 | 1078 | let toString = 1079 | fun 1080 | | `contentBox => "content-box" 1081 | | `borderBox => "border-box"; 1082 | }; 1083 | 1084 | module ColumnCount = { 1085 | type t = [ | `auto | `count(int)]; 1086 | 1087 | let toString = 1088 | fun 1089 | | `auto => "auto" 1090 | | `count(v) => Js.Int.toString(v); 1091 | }; 1092 | 1093 | module UserSelect = { 1094 | type t = [ | `none | `auto | `text | `contain | `all]; 1095 | 1096 | let toString = 1097 | fun 1098 | | `none => "none" 1099 | | `auto => "auto" 1100 | | `text => "text" 1101 | | `contain => "contain" 1102 | | `all => "all"; 1103 | }; 1104 | 1105 | module TextTransform = { 1106 | type t = [ | `none | `capitalize | `uppercase | `lowercase]; 1107 | 1108 | let toString = 1109 | fun 1110 | | `none => "none" 1111 | | `capitalize => "capitalize" 1112 | | `uppercase => "uppercase" 1113 | | `lowercase => "lowercase"; 1114 | }; 1115 | 1116 | module GridTemplateAreas = { 1117 | type t = [ | `none | `areas(list(string))]; 1118 | 1119 | let toString = 1120 | fun 1121 | | `none => "none" 1122 | | `areas(l) => 1123 | String.trim( 1124 | List.fold_left((carry, elem) => carry ++ "'" ++ elem ++ "' ", "", l), 1125 | ); 1126 | }; 1127 | 1128 | module GridArea = { 1129 | type t = [ 1130 | | `auto 1131 | | `ident(string) 1132 | | `num(int) 1133 | | `numIdent(int, string) 1134 | | `span([ | `num(int) | `ident(string)]) 1135 | ]; 1136 | 1137 | let toString = t => { 1138 | switch (t) { 1139 | | `auto => "auto" 1140 | | `ident(s) => s 1141 | | `num(i) => string_of_int(i) 1142 | | `numIdent(i, s) => string_of_int(i) ++ " " ++ s 1143 | | `span(e) => 1144 | "span " 1145 | ++ ( 1146 | switch (e) { 1147 | | `num(i) => string_of_int(i) 1148 | | `ident(s) => s 1149 | } 1150 | ) 1151 | }; 1152 | }; 1153 | }; 1154 | 1155 | module BackgroundAttachment = { 1156 | type t = [ | `scroll | `fixed | `local]; 1157 | 1158 | let toString = 1159 | fun 1160 | | `scroll => "scroll" 1161 | | `fixed => "fixed" 1162 | | `local => "local"; 1163 | }; 1164 | 1165 | module BackgroundClip = { 1166 | type t = [ | `borderBox | `paddingBox | `contentBox]; 1167 | 1168 | let toString = 1169 | fun 1170 | | `borderBox => "border-box" 1171 | | `contentBox => "content-box" 1172 | | `paddingBox => "padding-box"; 1173 | }; 1174 | 1175 | module BackgroundOrigin = { 1176 | type t = [ | `borderBox | `paddingBox | `contentBox]; 1177 | 1178 | let toString = 1179 | fun 1180 | | `borderBox => "border-box" 1181 | | `contentBox => "content-box" 1182 | | `paddingBox => "padding-box"; 1183 | }; 1184 | 1185 | module BackgroundRepeat = { 1186 | type twoValue = [ | `repeat | `space | `round | `noRepeat]; 1187 | type t = [ | `repeatX | `repeatY | twoValue]; 1188 | type horizontal = twoValue; 1189 | type vertical = twoValue; 1190 | 1191 | let toString = 1192 | fun 1193 | | `repeatX => "repeat-x" 1194 | | `repeatY => "repeat-y" 1195 | | `repeat => "repeat" 1196 | | `space => "space" 1197 | | `round => "round" 1198 | | `noRepeat => "no-repeat"; 1199 | }; 1200 | 1201 | module TextOverflow = { 1202 | type t = [ | `clip | `ellipsis | `string(string)]; 1203 | 1204 | let toString = 1205 | fun 1206 | | `clip => "clip" 1207 | | `ellipsis => "ellipsis" 1208 | | `string(s) => s; 1209 | }; 1210 | 1211 | module TextDecorationLine = { 1212 | type t = [ | `none | `underline | `overline | `lineThrough | `blink]; 1213 | 1214 | let toString = 1215 | fun 1216 | | `none => "none" 1217 | | `underline => "underline" 1218 | | `overline => "overline" 1219 | | `lineThrough => "line-through" 1220 | | `blink => "blink"; 1221 | }; 1222 | 1223 | module TextDecorationStyle = { 1224 | type t = [ | `solid | `double | `dotted | `dashed | `wavy]; 1225 | 1226 | let toString = 1227 | fun 1228 | | `solid => "solid" 1229 | | `double => "double" 1230 | | `dotted => "dotted" 1231 | | `dashed => "dashed" 1232 | | `wavy => "wavy"; 1233 | }; 1234 | 1235 | module Width = { 1236 | type t = [ | `auto | `fitContent]; 1237 | 1238 | let toString = 1239 | fun 1240 | | `auto => "auto" 1241 | | `fitContent => "fit-content"; 1242 | }; 1243 | 1244 | module MaxWidth = { 1245 | type t = [ | `none]; 1246 | 1247 | let toString = 1248 | fun 1249 | | `none => "none"; 1250 | }; 1251 | 1252 | module OverflowWrap = { 1253 | type t = [ | `normal | `breakWord | `anywhere]; 1254 | 1255 | let toString = 1256 | fun 1257 | | `normal => "normal" 1258 | | `breakWord => "break-word" 1259 | | `anywhere => "anywhere"; 1260 | }; 1261 | -------------------------------------------------------------------------------- /src/Css_Types.rei: -------------------------------------------------------------------------------- 1 | // Docs copied from MDN 2 | 3 | module Cascading: { 4 | type t = [ | `initial | `inherit_ | `unset]; 5 | 6 | let initial: [> t]; 7 | let inherit_: [> t]; 8 | let unset: [> t]; 9 | 10 | let toString: t => string; 11 | }; 12 | 13 | module Time: { 14 | /** 15 | The