Statically typed DSL for writing css in reason and rescript.
The bs-css library contains type css core definitions, it has different implementations:
- bs-css-emotion is a typed interface to Emotion
- bs-css-fela is a typed interface to Fela (react)
- bs-css-dom is a typed interface to
ReactDOMRe.Style.t
(reason-react)
If you know another implementation that can be added, send a link in an issue or create a PR.
npm install --save bs-css bs-css-emotion
or
yarn add bs-css bs-css-emotion
In your bsconfig.json
, include "bs-css"
and "bs-css-emotion"
in the bs-dependencies
.
You can replace bs-css-emotion
with bs-css-dom
in the above instructions if you prefer
to use React styles, or bs-css-fela
for a different runtime.
module Theme = {
let basePadding = CssJs.px(5)
let textColor = CssJs.black
}
module Styles = {
/*
Open the Css module, so we can access the style properties below without prefixing them with Css.
You can use either Css or CssJs: Css module is using lists, CssJs is using arrays.
If you're targeting js and/or using Rescript, prefer CssJs
*/
open CssJs
let card = style(. [
display(flexBox),
flexDirection(column),
alignItems(stretch),
backgroundColor(white),
boxShadow(Shadow.box(~y=px(3), ~blur=px(5), rgba(0, 0, 0, #num(0.3)))),
// You can add non-standard and other unsafe style declarations using the `unsafe` function, with strings as the two arguments
unsafe("-webkit-overflow-scrolling", "touch"),
// You can place all your theme styles in Theme.re and access as normal module
padding(Theme.basePadding),
])
let title = style(. [
fontSize(rem(1.5)),
lineHeight(#abs(1.25)),
color(Theme.textColor),
marginBottom(Theme.basePadding),
])
let actionButton = disabled =>
style(. [
background(disabled ? darkgray : white),
color(black),
border(px(1), solid, black),
borderRadius(px(3)),
])
}
@react.component
let make = () =>
<div className=Styles.card>
<h1 className=Styles.title> {React.string("Hello")} </h1>
<button className={Styles.actionButton(false)} />
</div>
Global css
You can define global css rules with global
open CssJs
global(. "body", [margin(px(0))])
global(. "h1, h2, h3", [color(rgb(33, 33, 33))])
Keyframes
Define animation keyframes;
open CssJs
let bounce = keyframes(. [
(0, [transform(scale(0.1, 0.1)), opacity(0.0)]),
(60, [transform(scale(1.2, 1.2)), opacity(1.0)]),
(100, [transform(scale(1.0, 1.0)), opacity(1.0)]),
])
let styles = style(. [
animationName(bounce),
animationDuration(2000),
width(px(50)),
height(px(50)),
backgroundColor(rgb(255, 0, 0)),
])
// ...
<div className=styles> {React.string("bounce!")} </div>
Css attributes
For some CSS parameters (like setting padding on an input field), one needs to use CSS attributes like so:
input[type="text"] {
padding:20px;
}
The selector
function can be used:
open CssJs
let styles = style(. [selector("input[type='text']", [padding(px(20))])])
You should avoid trying to merge styles in the same list of rules or by concatinating lists. A list of rules is converted into a JS object before being passed to Emotion where every property becomes a key in the object. This means you lose any earlier rule if you have another rule with the same property later in the list. This is especially noticable when writing sub-selectors and media queries
Trying to merge styles by just using concatenation can result in unexpected results.
This example:
open CssJs
let base = [
padding(px(0)),
fontSize(px(1))
]
let overrides = [
padding(px(20)),
fontSize(px(24)),
color(blue)
]
let media1 = [
media("(max-width: 768px)", [
padding(px(10))
])
]
let media2 = [
media("(max-width: 768px)", [
fontSize(px(16)),
color(red)
])
]
let mergedStyles = style(. Belt.Array.concatMany([base, overrides, media1, media2]))
generates the following:
.css-1nuk4bg {
padding: 20px;
font-size: 24px;
color: #0000ff;
}
@media (max-width: 768px) {
.css-1nuk4bg {
font-size: 16px;
color: #ff0000;
}
}
As you can see both properties from base
are overwritten (as opposed to overridden in css) and the media query in media1
is also lost because the media query from media2
overwrites it.
merge
safely merges styles by name. Uses Emotion’s cx
method.
open CssJs
let mergedStyles = merge(. [
style(. [padding(px(0)), fontSize(px(1))]),
style(. [padding(px(20)), fontSize(px(24)), color(blue)]),
style(. [media("(max-width: 768px)", [padding(px(10))])]),
style(. [media("(max-width: 768px)", [fontSize(px(16)), color(red)])]),
])
Generates the following:
.css-q0lkhz {
padding: 0px;
font-size: 1px;
padding: 20px;
font-size: 24px;
color: #0000ff;
}
@media (max-width: 768px) {
.css-q0lkhz {
padding: 10px;
}
}
@media (max-width: 768px) {
.css-q0lkhz {
font-size: 16px;
color: #ff0000;
}
}
Nothing is lost and everything ends up in the final stylesheet where normal overrides apply.
First you need to use a provider in your Jsx:
let renderer = createRenderer()
switch ReactDOM.querySelector("#app") {
| None => ()
| Some(dom) =>
ReactDOM.render(<CssReact.RendererProvider renderer> ... </CssReact.RendererProvider>, dom)
}
Then, you need to use the useFela hook in your Jsx:
module Styles = {
/*
Open the Css module, so we can access the style properties below without prefixing them with Css.
You can use either Css or CssJs: Css module is using lists, CssJs is using arrays.
If you're targeting js and/or using Rescript, prefer CssJs
*/
open CssJs
let card = style(. [
display(flexBox),
flexDirection(column),
alignItems(stretch),
backgroundColor(white),
boxShadow(Shadow.box(~y=px(3), ~blur=px(5), rgba(0, 0, 0, #num(0.3)))),
// You can add non-standard and other unsafe style declarations using the `unsafe` function, with strings as the two arguments
unsafe("-webkit-overflow-scrolling", "touch"),
// You can place all your theme styles in Theme.re and access as normal Reason module
padding(Theme.basePadding),
])
let title = style(. [
fontSize(rem(1.5)),
lineHeight(#abs(1.25)),
color(Theme.textColor),
marginBottom(Theme.basePadding),
])
let actionButton = disabled =>
style(. [
background(disabled ? darkgray : white),
color(black),
border(px(1), solid, black),
borderRadius(px(3)),
])
}
@react.component
let make = () => {
let {css, _} = CssReact.useFela()
<div className={css(. Styles.card)}>
<h1 className={css(. Styles.title)}> {React.string("Hello")} </h1>
<button className={css(. Styles.actionButton(false))} />
</div>
}
Global css
You can define global css rules with global
open CssJs
let renderer = createRenderer()
renderGlobal(. renderer, "body", [margin(px(0))])
renderGlobal(. renderer, "h1, h2, h3", [color(rgb(33, 33, 33))])
Use style instead of classname, for example:
module Styles = {
// Open the Css module, so we can access the style properties below without prefixing them with Css
open CssJs
let card = style(. [
display(flexBox),
flexDirection(column),
alignItems(stretch),
backgroundColor(white),
boxShadow(Shadow.box(~y=px(3), ~blur=px(5), rgba(0, 0, 0, #num(0.3)))),
// You can add non-standard and other unsafe style declarations using the `unsafe` function, with strings as the two arguments
unsafe("-webkit-overflow-scrolling", "touch"),
// You can place all your theme styles in Theme.re and access as normal Reason module
padding(Theme.basePadding),
])
let title = style(. [fontSize(rem(1.5)), color(Theme.textColor), marginBottom(Theme.basePadding)])
let actionButton = disabled =>
style(. [
background(disabled ? darkgray : white),
color(black),
border(px(1), solid, black),
borderRadius(px(3)),
])
}
@react.component
let make = () =>
<div style=Styles.card>
<h1 style=Styles.title> {React.string("Hello")} </h1>
<button style={Styles.actionButton(false)} />
</div>
You can check out Css_Js_Core.rei and Css_Legacy_Core.rei.
Thanks to emotion which is doing all the heavy lifting.
Thanks to bs-glamor which this repo was forked from.
Thanks to elm-css for dsl design inspiration.
bs-css's People
Forkers
cullophid steinararnason chenglou glennsl thangngoc89 jonfridrik arnarthor stefanmisic ebuall aleksion arnihermann avohq dckt knowbody fhammerschmidt rkoster bradleyayers danben4 laufeyrut kiraarghy maarekj remitbri brad-decker ctbucha cdebotton ambientlight bobzhang achillesjorgie ronyvidaur baransu joprice kkweon kpsuperplane mkly baldurh abeyonalaja lukashambsch sean-clayton togedo c19 rodrigo-morais jakubmarkiewicz izebb simonkberg rjhilgefort arielschiavoni nicholaslyang freddy03h esbullington figitaki amxfonseca elyanory reasonable-solutions kimjo77 kuy bwarni pet3ris gaku-sei aslamhadi drew887 jaredly ryb73 fakenickels lukiwolski idkjs erykpiast caulagi zalcode bloodyowl damianfral whythat paulshen arnarkari93 ridhoassuryadi mfakhrusy rizo cironunes bhansconnect kthorri rusty-key lorenzo-pomili rohea jeong-sik tomis abenoit 0m15 amirmoh10 happylinks ruslangrigoryev joseemds cknitt hugihlynsson jonathanazulay-sileon ashton rikimasan rolandpeelen imandra-ai sigurthorbs-css's Issues
Fraction unit (fr) for grid
I can't use fr
unit for gridTemplateColumns
/gridTemplateRows
.
e.g.
gridTemplateColumns([fr(1), fr(2)]), // <-- error
Error output
This has type:
[> `fr(float) ]
But somewhere wanted:
[ `auto
| `calc([ `add | `sub ], Css.length, Css.length)
| `ch(float)
| `cm(float)
| `em(float)
| `ex(float)
| `mm(float)
| `percent(float)
| `pt(int)
| `px(int)
| `rem(float)
| `vh(float)
| `vmax(float)
| `vmin(float)
| `vw(float)
| `zero ]
at <anonymous>nt type does not allow tag(s) `fr
Is fr
maybe missing from function string_of_dimension
?
DSL macro expansion
Hi guys, thanks for creating such a beginner-friendly CSS tool for Reason beginners like myself.
Are there any plans to improve the verbosity of the DSL through something like a macro expansion extension to Reason? https://github.com/cyrus-/relit
To a beginner, it feels like the holy grail of css-in-Reason is to write in plain (or nearly plain) css. Something like:
module Styles = {
open Css_notation;
let card = $css `
display: flexbox;
flex-direction: column;
align-items: stretch;
background-color: white;
box-shadow: 3px 5px rgba(0, 0, 0, 0.3);
padding: ${Theme.basePadding};
`;
};
I'm not sure what's technologically feasible here, so I'd be really interested to know whether this is worth looking into or just a total dead end. Thanks.
Losing styles when merging nested selectors
We're building components by composing different styles. However, if we use the same sub-selector in two places, only one will be compiled. Eg:
let buttonStyles = [
padding(px(5)),
media("(min-width: 768px)", [
padding(px(10)),
]),
]
let typographyStyles = [
fontSize(px(14)),
media("(min-width: 768px)", [
fontSize(px(16)),
]),
]
let Button = style(merge([
buttonStyles,
typographyStyles,
]))
In this case, only the buttonStyles
media query works. This happens for any sub-selector, eg &:hover
, &:focus
, [dir=rtl] &
.
Looking through the implementation, it seems like this issue is quite baked in, with how bs-css abstracts glamor objects as reason lists.
The example above may be arbitrary, but IMO a big draw for CSS-in-JS, and glamor especially, is composability. This bug and abstraction break it quite severely.
Rule list order should be preserved after Emotion.makeDict
On current master, the order of properties is backwards from how it ultimately ends up in CSS.
Css.[
background(red),
borderBottom(px(5), solid, black),
width(px(50)),
height(px(50)),
margin(px(10)),
];
margin: 10px;
height: 50px;
width: 50px;
border-bottom: 5px solid #000000;
background: #FF0000;
Presumably this is because here we reverse the rule list before passing it into the JS object creator.
I noticed this because of the interaction between properties like border and border left. Order matters in this case.
Changing the order to match existing CSS was part of #92, but was stripped out with the merge changes I'd added. I've been using my own fork because of this, but I'd like to have it patched upstream. What better than a major release?
boxSizing needs cascading values
Of course a lot of other properties need this as well, but right now I need it for boxSizing
! 😄
I’m writing some base css for my app and want this rule:
*, *::before, *::after { box-sizing: inherit; }
I want to be able to do it cleanly like so:
global("*, *::before, *::after", [boxSizing(`inherit)])
But right now I have to do:
insertRule("*, *::before, *::after { box-sizing: inherit; }")
Add support for commonly used name for font weight
Hi,
Would it be possible to add support for commonly used font weight, like the one described in the css reference, like "normal", "bold" ... ?
Thanks
Support for nesting children?
I've looked through the code and don't see this option, but I may have missed something. I think it would be pretty useful to be able to target any html tag ("a", "button", ".some-classname", etc) within the component using bs-css.
Support for `content` property on pseudo elements (::after, ::before)
I can't seem to find any way to assign a content property (empty string is what I'm going for) within a pseudo element.
If there is a way to do this already, would you mind pointing me in the right direction?
backgroundPosition(align, [align])
The current implementation of backgroundPosition
looks like this:
let backgroundPosition = (x, y) =>
d("backgroundPosition", string_of_length(x) ++ " " ++ string_of_length(y));
Unfortunately, this prevents things like backgroundPosition: center;
. Is it possible to support string_of_align()
here as well?
Add support of column-count
It would be great to have support for column-count.
https://developer.mozilla.org/en-US/docs/Web/CSS/column-count
Possible to use multiple background layers?
I'm trying to an image url on top of a linear gradient as a background, but it seems like the type system is limiting me to one or the other. Is there a way to use multiple background layers with bs-css?
E.g. generate something like this:
background: url("../img/bg-pattern.png"), linear-gradient(to left, #7b4397, #dc2430);
Use emotion instead of glamor as the backing library
The last commit for glamor was over a year ago..
In the issue thread linked above, the maintainer mentions that emotion has mostly the same API. This mostly seems to be true, the differences being stuff around rule insertion and @fontface
.
Update README with v8 changes
Changes:
- Emotion is now the backing library. If you're using tools like browserslist, you should see less unnecessary autoprefixing. #94
-
merge
no longer merges alist(list(rule))
and instead only mergeslist(string)
into a string. If you need the old list behavior,List.concat
has you covered. #97 - Rule lists are applied in the order they are provided instead of reversed like they were in v7. #96
- Font weight now requires the use of the num polymorphic variant instead of just ints. #98
inherit/unset values
I'm trying to add these values to the different properties, but it's a very long work to add it to every css prop.
- I'd like to know what are the most important properties to start with, and focus on these first.
- I also have a problem when using
inherit
because it's a keyword. I'm usinginherit_
right now, but I'd like to know if someone has a better suggestion.
thanks.
What is the way of generating (min-width: 960px) with bs-css
Hi folks,
I was using Css.media(string, Css.rule) and wanted to compute (min-width: 960px) with bs-css.
Initially my thought was that was possible because internally we have string_of_length, we also have minWidth.
After trying a bit I did not found a way to do that.
Thank you for wrapping Glamor.
This is my first css in js (reason actually) experience.
Expose `string_of` functions
It might be a good idea to expose the string_of_length
and similar functions as sometimes they are needed. Normally when you're down to the definition
level and you have constants defined elsewhere as length
. At the moment they are hidden from the interface and I literally grabbed them from the source. But this way if bs-css changes my forked functions will get out of sync.
These functions are also useful when you have a theme and you need to add some inline styles.
I would just alias the functions within a Utils submodule. I can make a pull request if you think it's a good idea
SSR?
With this support server-side rendering?
Looking for (co)maintainers
First of all Thanks to everyone who are using and or contributing to bs-css!
I am sorry that it often takes a while before I get to answer issues, or review pull requests.
Between Baby and startup I dont have much time on my hands.
If any of you are using bs-css and would like to help maintain the project (answer issues, review/merge PR's) I would really appreciate it.
I will ofc still be here as much as i can.
Can't use empty string in contentRule
This rule doesn't work:
style([ contentRule("") ])
it produces the following css:
content:
The workaround is to use:
style([ contentRule("\"\"") ])
Isn't the style call on render being executed at each render pass ?
flexShrink and flex should accept a float instead of a int
Same issue as flexGrow (#105)
lineHeight unit
What unit to use for lineHeight? I'm trying to pass 1.3, but px requires whole numbers, what unit should be used? Not even sure if it should have been px as in css we don't add px to the end of the value.
How to use glamor plugins?
Would like to use the increase-specificity.js
plugin, to fix a conflict with material-ui-next. Would this be something we could inject here?
Support elm-css's property function
elm-css supports letting users embed any CSS declaration with an api like such:
property "grid-template-areas" """
"header header header"
"sidebar content content"
"footer footer footer"
"""
property "grid-area" "content"
It's a nice escape hatch for when properties or values don't have type definitions so users can sacrifice a little type-safety to not be blocked by lack of existing definitions in the library.
I looked at the source and it seems the d
function already does this, but isn't exposed in Css.rei
--I could maybe create a PR, but I'm not quite sure how to test it all.
The elm-css implementation:
CSS grid not defined
Hello!
grid
is a valid value for display
, but it does not seem to be defined.
Would you accept a PR adding this?
Thanks,
Louis
use 'unset' or 'inherit' values
Hi,
how can I use these special values ?
I want a font-size: unset
rule, but I don't know how to do better than this: style([unsafe("fontSize", "unset")])
.
I'm on [email protected]
thanks
Add repeat to gridLength
Maybe an approach similar to this would be helpful. But I'm bogged down in polymorphic variants trying to patch this up myself.
The implementation src/Css.mlast
does not match the interface src/css.cmi:
Values do not match:
let gridTemplateColumns:
list(([< `auto
| `calc([< `add | `sub ],
([< `calc([< `add | `sub ], 'b, 'b) &
([< `add | `sub ], 'b, 'b)
| `ch(float)
| `cm(float)
| `em(float)
| `ex(float)
| `mm(float)
| `percent(float)
| `pt(int)
| `px(int)
| `pxFloat(float)
| `rem(float)
| `vh(float)
| `vmax(float)
| `vmin(float)
| `vw(float)
| `zero ]
as 'b),
([< `calc([< `add | `sub ], 'c, 'c) &
([< `add | `sub ], 'c, 'c)
| `ch(float)
| `cm(float)
| `em(float)
| `ex(float)
| `mm(float)
| `percent(float)
| `pt(int)
| `px(int)
| `pxFloat(float)
| `rem(float)
| `vh(float)
| `vmax(float)
| `vmin(float)
| `vw(float)
| `zero ]
as 'c))
| `ch(float)
| `cm(float)
| `em(float)
| `ex(float)
| `fr(float)
| `maxContent
| `minContent
| `mm(float)
| `percent(float)
| `pt(int)
| `px(int)
| `pxFloat(float)
| `rem(float)
| `repeat([< `autoFill | `autoFit | `n(int) ], 'a)
| `vh(float)
| `vmax(float)
| `vmin(float)
| `vw(float)
| `zero ]
as 'a)) =>
[> `declaration(string, string) ]
is not included in
let gridTemplateColumns:
list([ `auto
| `calc([ `add | `sub ], length, length)
| `ch(float)
| `cm(float)
| `em(float)
| `ex(float)
| `fr(float)
| `maxContent
| `minContent
| `mm(float)
| `percent(float)
| `pt(int)
| `px(int)
| `pxFloat(float)
| `rem(float)
| `repeat(repeatValue, gridLength)
| `vh(float)
| `vmax(float)
| `vmin(float)
| `vw(float)
| `zero ]) =>
rule
from
diff --git a/src/Css.re b/src/Css.re
index 1963f48..e3e4067 100644
--- a/src/Css.re
+++ b/src/Css.re
@@ -3,8 +3,7 @@ include Css_Colors;
module Emotion = {
type css = string;
[@bs.module "emotion"] external _make: Js.Json.t => css = "css";
- [@bs.module "emotion"]
- external injectGlobal: Js.Json.t => unit = "";
+ [@bs.module "emotion"] external injectGlobal: Js.Json.t => unit = "";
[@bs.module "emotion"]
external rawInjectGlobal: string => unit = "injectGlobal";
[@bs.module "emotion"]
@@ -291,9 +290,12 @@ type selector = [ | `selector(string, list(rule))];
let empty = [];
let merge = List.concat;
-let global = (selector, rules: list(rule)) => {
- Emotion.injectGlobal([(selector, Emotion.makeDict(rules))]->Js.Dict.fromList->Js.Json.object_);
-}
+let global = (selector, rules: list(rule)) =>
+ Emotion.injectGlobal(
+ [(selector, Emotion.makeDict(rules))]
+ ->Js.Dict.fromList
+ ->Js.Json.object_,
+ );
let insertRule = raw => Emotion.rawInjectGlobal(raw);
@@ -391,7 +393,21 @@ type length = [
| `zero
];
-type gridLength = [ length | `fr(float) | `minContent | `maxContent];
+type repeatValue = [ | `autoFill | `autoFit | `n(int)];
+
+let repeatValueToJs =
+ fun
+ | `autoFill => "auto-fill"
+ | `autoFit => "auto-fit"
+ | `n(x) => x->string_of_int;
+
+type gridLength = [
+ length
+ | `fr(float)
+ | `minContent
+ | `maxContent
+ | `repeat(repeatValue, gridLength)
+];
let string_of_length_cascading =
fun
@@ -757,7 +773,7 @@ let paddingRight = x => d("paddingRight", string_of_length(x));
let paddingTop = x => d("paddingTop", string_of_length(x));
let paddingBottom = x => d("paddingBottom", string_of_length(x));
-let string_of_dimension =
+let rec string_of_dimension =
fun
| `auto => "auto"
| `calc(`add, a, b) =>
@@ -781,7 +797,9 @@ let string_of_dimension =
| `fr(x) => string_of_float(x) ++ "fr"
| `zero => "0"
| `minContent => "min-content"
- | `maxContent => "max-content";
+ | `maxContent => "max-content"
+ | `repeat(n, x) =>
+ "repeat(" ++ repeatValueToJs(n) ++ ", " ++ string_of_dimension(x) ++ ")";
let width = x => d("width", string_of_dimension(x));
let maxWidth = x => d("maxWidth", string_of_dimension(x));
@@ -815,8 +833,8 @@ let gridAutoFlow = direction =>
let string_of_dimensions = dimensions =>
dimensions |> List.map(string_of_dimension) |> String.concat(" ");
-let gridTemplateColumns = dimensions =>
- d("gridTemplateColumns", string_of_dimensions(dimensions));
+let gridTemplateColumns =
+ dimensions => d("gridTemplateColumns", string_of_dimensions(dimensions));
let gridTemplateRows = dimensions =>
d("gridTemplateRows", string_of_dimensions(dimensions));
Need to be able to set pixels as floating point values
We’re creating a fluid grid system which requires floating point pixels since spacing and sizes are calculated values. What would be the best option? Changing `px
to accept float would be a breaking change. So maybe adding `pxFloat
?
Can't use it as a library
[email protected] is dependent on bs-platform@^3.0.0 but it is published with js files compiled with the 2.2.2 version.
The published js files are still using Pervasives.string_of_int that doesn't exist anymore in bs 3.3.0.
This is breaking my code when I use it in a transitive dependency.
lineheight unit
Today, lineHeight has the type float => rule
But lineHeight is supposed to be able to receive pixel, rem, em etc., in addition to the float.
It would be better if lineHeight had the following type: [ lenght | `abs(float) ]
Media queries
Can't seem to get them working, are there any examples of how to use it?
Use `num instead of `n for repeat
Compose
Is there a good way to compose multiple styles?
Selectors resolving to [object Object]
v6.1.0, though I believe this affected v5 as well.
I'm having trouble using selectors
:
let table = style([
selector("span", [
margin(unit(1.0))
])
]);
<div className=Styles.table>
This results in:
Any ideas on how I can troubleshoot?
Thanks for a great project!
Bind to `insertRule`
Unless it was left out of scope for a reason, I can prepare PR.
flexGrow should accept a float instead of a int
I think flexGrow should accept a float value instead of an int. When using css, I can set it to a float like .1 and it is valid css.
No List Style?
Couldn't find a listStyle()
function
[Bug] `type css` is aliased to string
The actual type is an object: https://github.com/threepointone/glamor/blob/667b480d31b3721a905021b26e1290ce92ca2879/index.d.ts#L268
This causes the following bug: MoOx/phenomic#1220
Where a style object is directly passed to className but it's not an actual string etc.
cc @thangngoc89
Add grid-auto-flow binding
There should probably be a binding for https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow.
Add fill and other properties for svg?
Hello! 👋
Do you think is a good idea to add some props like fill, stroke for svg?
fontSize does not support absolute and relative sizes
According to the formal syntax for font-size, it's supposed to support absolute and relative sizes like large, larger, etc.
Would you agree that bs-css needs types for absolute and relative sizes so that they can be supported by fontSize, or have you already thought through this?
I think typed css in reason has lots of potential, and that this project would flourish with more contributors and users. I'm still learning the language but I'll try and dedicate time to this.
[Question] Handling JS string color
Hello,
I'm asking myself about how to handle string color coming from the JavaScript side.
Is there an escape hatch for this ?
Thanks :)
Support division and multiplication with Calc
It would be nice to support the division and multiplication with Calc.
Wrong value for minWidth/maxWidth
We have
let minWidth: [ length | `auto] => rule;
let maxWidth: [ length | `auto] => rule;
"auto" is a valid keyword value for "min-width", but invalid or "max-width".
"none" is a missing keyword value for "max-width".
So we should have
let minWidth: [ length | `auto] => rule;
let maxWidth: [ length | `none] => rule;
Support for `inherit` value
Inherit is a useful value with properties like borderRadius
, borderColor
, fontSize
, etc...
If there is a way to do this already, would you mind pointing me in the right direction?
Colon-like infix operator
I know most people don't like operators, but since it's DSL, I wanted to share idea.
We have this fat and ugly operator ( @@ )
, that is perfect for reducing number of parentheses on function call, so instead of this:
let card = style([
display(flexBox),
flexDirection(column),
alignItems(stretch),
backgroundColor(white),
]);
we get this:
let card = style([
display @@ flexBox,
flexDirection @@ column,
alignItems @@ stretch,
backgroundColor @@ white,
]);
Which looks a bit nicer, but far from perfect.
We can define a better operator, that behaves exactly like ( @@ )
, but looks more CSS-like and lightweight:
let card = style([
display^: flexBox,
flexDirection^: column,
alignItems^: stretch,
backgroundColor^: white,
]);
We can define it like this:
let ( ^: ) = ( @@ );
Or like this:
external ( ^: ) : ('a => 'b) => 'a => 'b = "%apply";
polymorphic variant in rei
should we use [< | `some_tag | ... ]
instead of [ | `some_tag | ... ]
whenever possible?
https://github.com/SentiaAnalytics/bs-css/blob/master/src/Css.rei#L499 like here,
I've got a [<| `color | `url(string)]
, but when I pass it into background
, it could not compiles:
This has type:
[< `currentColor
| `hex(string)
| `hsl(int, int, int)
| `hsla(int, int, int, int)
| `rgb(int, int, int)
| `rgba(int, int, int, int)
| `transparent
| `url(string)
> `hex ]
But somewhere wanted:
[ `currentColor
| `hex(string)
| `hsl(int, int, int)
| `hsla(int, int, int, float)
| `linearGradient(Css.angle, list((int, Css.color)))
| `none
| `radialGradient(list((int, Css.color)))
| `repeatingLinearGradient(Css.angle, list((int, Css.color)))
| `repeatingRadialGradient(list((int, Css.color)))
| `rgb(int, int, int)
| `rgba(int, int, int, float)
| `transparent
| `url(string) ]
CSS grid support
Hey, I'm going to submit a PR with some CSS grid values that are currently not supported.
One minor problem that needs to be decided on before I do so is if it should be a breaking change or not, but as a consequence not give compile time errors for the difference between flex and grid.
The problem is that both Flex and CSS grid use the align-items
key to tell how to align items within a grid or flex container.
For flex box the valid values for this property are:
normal;
stretch;
center;
start;
end;
flex-start;
flex-end
self-start;
self-end;
baseline;
first baseline;
last baseline;
safe center;
unsafe center;
inherit;
initial;
unset;
But for grid they are just a subset of these
auto
normal
start
end
center
stretch
baseline
first baseline
last baseline
Should grid just not have the same compile time guarantees and just add the missing fields for that. Or should the user specify that its using Flex by doing something like alignItems(`flex(`flexEnd))
?
I'm leaning towards non compile guarantees since the CSS grid properties are just a subset of flex (apart from auto
), but at the same time it breaks the contract of typesafe CSS in the library.
All thoughts greatly appreciated. I'm really torn here 😄
Should we add generated js files to .gitignore?
@giraud what do you think about removing these files from git?
The benefits, I think, are
- Smaller diffs and PRs
- Easier time merging
But I can see that adding the compiled JS means bs-css's users can
- try the example/index page without building
- get a feel for how heavy the library code is without node_module dependencies
Proposal: A better type system for values
I've been toying a bit with the type system to get value types to compose better. This will make declarations more concise, but will also solve some issues that I don't think can be solved with the current approach. AND I actually think the errors this produces is better.
The problem
CSS is typed as lots of nested unions. These unions are currently modeled in one of two ways:
- As string-backed abstract types, e.g.
cssunit
,color
. These are efficient and concise, but do not nest and need to be uniquely named. - As ordinary vairants, e.g.
background
. These nest and do not need to be uniquely named, but are inefficient and verbose.
A common problem for both approaches is that they need to enumerate every single possible value of that type, which for 1. is practically impossible due to the need for unique names, and for 2. makes the implementation very verbose and repetitive.
More specifically, the practical problem is with implementing these CSS features:
- The universal values,
inherit
,initial
andunset
, which should be appllicable to every css property. - Nested unions can become pretty verbose, either when used or when implemented (or a bit of both, as it is now). For example, a DRY implementation of
background
would have usage look likebackground(Image(Url(url("http://..."))))
. Alternatively it can be "flattened" to justbackground(Url("http://..."))
, but at the cost of needing to explicitly enumerate and convert all possible values in the implementation of each property.
Proposal
By using a phantom polymorphic variant type, we can model this more precisely.
This is what it could look like in use:
Css.(style([
background(white),
background(url("http://...")),
background(linearGradient(...)),
color(unset),
]));
Compared to today:
Css.(style([
background(Color(white)),
background(Image("http://...")),
/* gradients currently aren't valid images, so including them would require adding another
* level (which would also make the url declaration above more verbose), or alternatively
* another variant constructor if flattened
*/
background(Image(Gradient(linearGradient(...)))),
/* not implemented today, but could be hacked together to look like this and be somewhat
* type-safe
*/
unset(color),
]));
Implementation
Time for magic. Let's first look at the interface:
/* The type of all css values */
type value('a);
/* [`color] is the type of a polymorphic variant with a single constructor, `color */
type color = value([`color]);
let white: color;
/* The aliases aren't strictly necessary, but gives nicer error messages */
type url = value([`url]);
let url: string => url;
/* [< `color ...] means we'll accept any polymorphic variant type containing a subset of
* these constructors. So value([`color]) is good, as would value([`color | `url]) be,
* but not value([`color | `length])
*/
type background('a) = value([< `color | `url | `gradient] as 'a);
let background: background('a) => rule;
To implement it, we'll define a couple of helpers to avoid blind casting magic and ensure what we put in and get out are actually strings, as expected:
external makeValue : string => value(_) = "%identity";
external getValue : value(_) => string = "%identity";
We'll also represent the rule as just a tuple for simplicity:
type rule = (string, string);
And then the meat of the implementation:
type value('a);
type color = value([`color]);
let white = makeValue("white");
type url = value([`url]);
let url = url => makeValue({j|url($url)|j});
type background('a) = value([< `color | `url | `gradient] as 'a);
let background = value => ("background", getValue(value));
A playground with this code, as well as an example of how to define a universal value
Conclusion
More testing is of course needed to ensure there aren't any problems when things get more complex, or if CSS does something super weird that doesn't fit this model either. But I think this approach is worth exploring further. If everything works out it's looking to be more concise, flexible, efficient and give better errors, but at the cost of making the implementation a bit harder to understand.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.