giraud / bs-css Goto Github PK
View Code? Open in Web Editor NEWStatically typed DSL for writing css in reason.
License: ISC License
Statically typed DSL for writing css in reason.
License: ISC License
Couldn't find a listStyle()
function
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 π
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
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
?
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.
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
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:
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) ]
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));
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?
Hello! π
Do you think is a good idea to add some props like fill, stroke for svg?
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.
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?
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!
It would be nice to support the division and multiplication with Calc.
Is there a good way to compose multiple styles?
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.
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;
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
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.
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";
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.
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.
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?
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);
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
?
This rule doesn't work:
style([ contentRule("") ])
it produces the following css:
content:
The workaround is to use:
style([ contentRule("\"\"") ])
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
Can't seem to get them working, are there any examples of how to use it?
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.
CSS is typed as lots of nested unions. These unions are currently modeled in one of two ways:
cssunit
, color
. These are efficient and concise, but do not nest and need to be uniquely named.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:
inherit
, initial
and unset
, which should be appllicable to every css property.background
would have usage look like background(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.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),
]));
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
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.
Unless it was left out of scope for a reason, I can prepare PR.
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?
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
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
.
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?
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) ]
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; }")
Same issue as flexGrow (#105)
With this support server-side rendering?
There should probably be a binding for https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow.
[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.
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.
inherit
because it's a keyword. I'm using inherit_
right now, but I'd like to know if someone has a better suggestion.thanks.
It would be great to have support for column-count.
https://developer.mozilla.org/en-US/docs/Web/CSS/column-count
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.
@giraud what do you think about removing these files from git?
The benefits, I think, are
But I can see that adding the compiled JS means bs-css's users can
Changes:
merge
no longer merges a list(list(rule))
and instead only merges list(string)
into a string. If you need the old list behavior, List.concat
has you covered. #97What 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.
Hello,
I'm asking myself about how to handle string color coming from the JavaScript side.
Is there an escape hatch for this ?
Thanks :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.