Code Monkey home page Code Monkey logo

davesnx / styled-ppx Goto Github PK

View Code? Open in Web Editor NEW
399.0 9.0 32.0 9.68 MB

Type-safe styled components for ReScript, Melange and native with type-safe CSS

Home Page: https://styled-ppx.vercel.app

License: BSD 2-Clause "Simplified" License

Reason 32.03% OCaml 9.35% JavaScript 7.89% Shell 0.05% CSS 0.64% HTML 0.02% ReScript 0.67% Raku 29.33% Perl 5.11% Makefile 0.19% MDX 1.93% TypeScript 12.78%
ocaml ppx css styled-components emotion styled css-in-js vscode-extension react reasonml

styled-ppx's Introduction

Hi πŸ‘‹

I'm a software engineer based in Barcelona working with Reason/OCaml and exploring the world of functional programming. I also co-host emelle.tv, a talk show about these languages.

Check my site sancho.dev.

twitter


Let me know if you like my work

styled-ppx's People

Contributors

an146 avatar davesnx avatar denis-ok avatar dependabot[bot] avatar dylanirlbeck avatar eduardorfs avatar feihong avatar ixzzd avatar jchavarri avatar jfrolich avatar joseemds avatar lorenzo-pomili avatar lubegasimon avatar manasjayanth avatar mnxn avatar pedrobslisboa avatar psb avatar rusty-key avatar yatesco avatar zakybilfagih avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

styled-ppx's Issues

Add Global Styles API

Emotion supports inject global styles, we can support similar API:

[%styled.global
  html, body {
    margin: 0;
  }
];

Components with styles based on props that are defined statically

module Component = [%styled
  (~size) => {
    if (size > 0) {
      "display: flex"
    }
  }
];
  • Change the PPX detection, recursively look for any sort of function body that returns a string, turn that as the body of the styled fn.
  • Define an API where you can pass more than one fn?
module Component = [%styled
  (~size) => {
    if (size > 0) {
      "display: flex"
    }
  }

  (~kind) => switch (kind) {
    | Solid => "padding: 10px"
	| _ => "padding: 2px 4px
  }
];

Support complex media queries values

Basic media queries are supported.

There are a few values that aren't

@media all and (max-device-width: 218px)
           and (max-device-height: 281px)
           and (orientation: portrait) {
  color: green;
}
  • device-width
  • device-height
  • max-device-width
  • max-device-height
  • orientation
  • -webkit-device-pixel-ratio
  • -webkit-min-device-pixel-ratio
  • @media only screen
  • @media print
  • @media all
  • min-color-index
  • ...

Remove Object.magic and mitigate Object.assign from props

Also, a few other notes: You should not use Obj.magic without specifying the type on iether side. Not doing so breaks the type system.
And you should probably not be using @bs.deriving abstract. That generates a lot of crap you probably do not want, and has some limitations as well. Defining plain object types and using ordinary object creation would be a lot simpler and more flexible.
With one caveat, @bs.derving abstract is better in one case, when you want users to be able to specify field names that are invalid identifiers in Reason. But that benefit falls away when you write a PPX, becasue you're not restricted by the syntax.
Or the lexer, at least.

You stilll do. Js.Obj.assign will restrict it to a Js.t object, but nothing more than that. It still also only constrains one side of it.

Right, yeah I think that might be unavoidable, regardless of method.

Because not speficying an optional argument should omit the field entirely, not just set it to undefined
The only way I found is doing the Obj.magic ofc
Which only works because it breaks the type system.
As does using Js.Obj.assign btw.

They can be okay to use internally, but you should constrain the types on either side externally
Otherwise all bets are off.
And I don't think you can do so here, unless you require the consumer to annotate both the input and output type of the props.
If you don't it'll be completely valid to have an input props type of Js.t({. apples: int }) and an output props type of Js.t({ pears: string })
Both are objects, but otherwise they need have no realtion to each other

Using assign to make a copy is fine, but you still shouldn't need Obj.magic. assign will take any Js object. Obj.magic should be used with extreme care, it's not a band-aid to be slapped on whenever the type system complains.
And personally I don't trust code that uses Obj.magic without being extremely explicit about what it does.
Which means I can't really help with any other type-related issue that touches this code, since Obj.magic breaks all the rules.
As does Js.Obj.assign to some extent. That should be extremely explicit as well.

assign and magic don't turn the type into anything. They just ignore it and let the compiler infer whatever it wants for either side.
You can restrict that if you annotate the types though, but you need to generate the types for annotation.
let newProps = Js.Obj.assign(Js.Obj.empty(), Obj.magic(props));
I don't udnerstand the purpose of Obj.magic here. assign will accept any object, and in this case it just creates a copy anyway, which is rerpesentable in the type system
This is where you actually change it: 
Js.Obj.assign(newProps, stylesObject),
So you want the return type to be annotated as the combination of the type of newProps and stylesObject.
The type system doesn't support types being combined like that, but if you have the AST for both types you could generate it syntactically.
Like, if you have 
type props = {. href: string };
type stylesObject = {. color: Emotion.Color.t };
 you could mash them together using ppx magic to get
type combined = {. href: string, color: Emotion.Color.t };

Publish it somewhere

  • I need it so, I created this.
  • Why I think it's a great tool.
  • https://css-tricks.com/why-is-css-frustrating
  • Still so much to do.
  • Has been an amazing experience.
  • Happy to create tools for the community.
  • Thanks to Javier ChΓ‘varri, for helping me understand all the OCaml world and his knowledge about ppx
  • Inspired to astrada (for bs-css-ppx).

Note on discord:

@Alexander I'm not looking at a utility-class based styling for mainly 2 reasons: First learning curve and second because it goes against type-inferred languages mentality.
Since you are allowed to define types once and not care where those data structures go, I find myself using bs-emotion directly trying to play arround types and values too often
I'm very awrae that CSS it's too flexible, but trying to fix it with functions makes writing styles a cumbersome.
Right, but maybe for simple css definitions works ok, but for background-gradient, keyframes, or even border-radius becomes much much harder.
and I'm sure that large frontend apps solve the problem of scalabilty doing some design system/UI/some sort of API-based not a CSS approach.

Error While Running Demo

Hi @davesnx. Thanks for sharing this work.

I am trying to run the demo unsuccessfully.

I have cloned the demo from this repo and followed the steps from the readme after running it out of the box did not work.

Here is the reproduction: https://github.com/idkjs/styled-ppx-master-demo

I have run yarn global add @davesnx/styled-ppx @ahrefs/bs-emotion and added "ppx-flags": ["@davesnx/styled-ppx/styled-ppx", "@ahrefs/bs-emotion"] to bsconfig.json.

Upon running yarn run re:watch I get the following compiler error which I am not able to resolve:

➜  styled-ppx-master-demo [master]yarn run re:watch
yarn run v1.22.0
warning package.json: No license field
$ bsb -make-world -w
bsb: no work to do.
bsb: no work to do.
bsb: no work to do.
>>>> Start compiling 
[1/3] Building src/index.reast
FAILED: src/index.reast 
/Users/prisc_000/Downloads/styled-ppx-master-demo/node_modules/bs-platform/lib/bsc.exe  -w -30-40+6+7+27+32..39+44+45+101 -color always -bs-jsx 3 -ppx /Users/prisc_000/Downloads/styled-ppx-master-demo/node_modules/@davesnx/styled-ppx/styled-ppx -ppx /Users/prisc_000/Downloads/styled-ppx-master-demo/node_modules/@ahrefs/bs-emotion  -o src/index.reast -bs-syntax-only -bs-binary-ast /Users/prisc_000/Downloads/styled-ppx-master-demo/src/index.re
sh: /Users/prisc_000/Downloads/styled-ppx-master-demo/node_modules/@ahrefs/bs-emotion: is a directory

  We've found a bug for you!
  /Users/prisc_000/Downloads/styled-ppx-master-demo/src/index.re
  
  Error while running external preprocessor
Command line: /Users/prisc_000/Downloads/styled-ppx-master-demo/node_modules/@ahrefs/bs-emotion '/var/folders/dz/xzttny_14gs2lgxcvl5lm5680000gn/T/camlppx947ec0' '/var/folders/dz/xzttny_14gs2lgxcvl5lm5680000gn/T/camlppx74f477'

  
FAILED: subcommand failed.
>>>> Finish compiling(exit: 1)

Any guidance would be appreciated. Thank you.

Find beta users

Look for people who have been asking on Discord & Twitter about styled-components/emotion bindings or any CSS in ReasonML.

CSS Property support

This is the umbrella issue of CSS property support.

This is the first chunk of properties that are half-supported and I think are a minimum to create any sort of styles.

Supporting those properties correctly we might unlock many more, like supporting opacity you support: stroke-opacity stop-opacity flood-opacity fill-opacity.

  • animation
  • box-sizing
  • box-shadow
  • text-shadow
  • transform
  • transition
  • font-family
  • font-weight
  • border-top-right-radius
  • border-top-left-radius
  • border-bottom-right-radius
  • border-bottom-left-radius
  • background-position
  • transform-origin
  • flex
  • border
  • outline
  • grid
  • grid-template-area

There are some of them that might be tricky and some very straight forward.

Theming draft

The initial idea of theming is to allow users to specify minimum design values that are shared across all components, so can be consumed.

styled-components/emotion have solved that with ThemeProvider and theme object is there under props.theme and all the logic of context is under the lib.

const theme = {
  fg: "palevioletred",
  bg: "white"
};

const Button = styled.button`
  color: ${props => props.theme.fg};
  border: 2px solid ${props => props.theme.bg};
`;

Note. Currently bs-emotion doesn't support that, so, this would include to create the bindings on https://github.com/ahrefs/bs-emotion as https://emotion.sh/docs/emotion-theming#themeprovider-reactcomponenttype

tailwind on the other side works only with classNames, so their solution it's superior on the sense of having more logic on the creation of those design values. You define a minimum set of spacing/textsize/colors and they generate a scalar css classNames in a super-long css file.

module.exports = {
  important: true,
  theme: {
    fontFamily: {
      display: ['Gilroy', 'sans-serif'],
      body: ['Graphik', 'sans-serif'],
    },
    extend: {
      colors: {
        cyan: '#9cdbff',
      },
      margin: {
        '96': '24rem',
        '128': '32rem',
      },
    }
  },
  variants: {
    opacity: ['responsive', 'hover']
  }
}

I like the approach.

Check https://github.com/system-ui/theme-specification and https://github.com/kripod/glaze

Create dynamic components

Right now we only support components defined with "static" CSS. Where "static" means only working with strings or multi-line strings:

module ComponentMultiline = [%styled
  {|
    color: red;
    background-color: white;
    margin: auto 0 10px 1em;
    border-bottom: thin dashed #eee;
    border-right-color: rgb(1, 0, 1);
    width: 70%;
    background: url(http://example.com/test.jpg);
  |}
]

Dynamic would be something like, instead of receiving a string, we would need to add a case where the ppx checks for a function and tries to create the make fn with the props that are being passed to that fn.

module StyledComponentWithProps = [%styled
  (~color) => {|
    color: $color;
  |}
];

Would turn to be:

module StyledComponentWithProps = {
  let styled = (~color) => Emotion.(css(color $color));

  [@react.component]
  let make = (~color, ~children?) => {
    <div className=styled(~color)>
      {switch (children) {
        | Some(c) => c
        | None => React.null
      }}
    </div>
  }
];

As a reference, we would need to do similar logic from react's ppx in order to create the styled and make fns.

Here is the jsofocaml:
https://github.com/jchavarri/jsoo-react/blob/master/ppx/Rroo_jsoo_ppx.re#L213-L658

Support all custom-renders from bs-css-ppx

This is the first chunk of properties that are half-supported and I think are a minimum to create any sort of styles. Rendering those properties correctly we would unlock many more, like supporting opacity you unlock: stroke-opacity stop-opacity flood-opacity fill-opacity.

  • animation
  • box-shadow
  • text-shadow
  • transform
  • transition
  • font-family
  • z-index
  • opacity
  • flex-grow
  • flex-shrink
  • font-weight
  • padding
  • margin
  • border-top-right-radius
  • border-top-left-radius
  • border-bottom-right-radius
  • border-bottom-left-radius
  • background-position
  • transform-origin
  • flex
  • border
  • outline

Support reference to other components

Parse nested properties and be able to create styles dependant on other's components.

module Children = [%styled "display: block;"];
module Parent = [%styled {|
  $Children.styled {
    color: red;
  }
|}];

`$Children.styled` should be available. 
(This would only work in static components, warn that styled isn't available, even the compiler will say it).

/* ... */
<Parent>
  <Children />
</Parent>

Support CSS functions

Functions are defined in the parser but aren't allowed in any property. Using any of those properties will break:

  • calc
  • max/min
  • minmax
  • attr
  • blur
  • radial-gradient() and repeating-radial-gradient()
  • clamp
  • repeat (#146)

Support a way to define any HTML tag

First I thought the syntax would work like that:

  • [%styled.div ""]; But there are a few limitations: Can't define dynamically the HTML tag. Add noise to the api, and It's a little bit complex to create those ppxs. Allows that It would remove the ability to styled(Component).
  • In styled-components they use "as" prop in the render. I like that and is reasonable simple to implement in the ppx.
  • I don't have many other ideas, so, feel free to discuss here.

Fix post-install script

[0]   We've found a bug for you!
[0]   /Users/davesnx/dev/try-styled-ppx/src/pages/Page_Home.re
[0]
[0]   External preprocessor does not produce a valid file
[0] Command line: styled-ppx '/var/folders/jr/74_11td95k9420cc55tl9h_80000gn/T/camlppxee7eee' '/var/folders/jr/74_11td95k9420cc55tl9h_80000gn/T/camlppx1ee053'

Create some tests in BuckleScript

Wanted to create some snapshot testing with Jest, but bs-jest doesn't support it. Found not the best option to do snapshot testing of the compiled code.

Try zero run-time costs

We will have the bridge between CSS AST to bs-emotion completed, we can port that bridge to a solution that doesn't cause run-time overhead, making styled components way more performant (theoretically, I would love to benchmark it first).

The steps are, try to understand what it means 0 runtime cost with dynamic styling https://compiledcssinjs.com/ or https://github.com/callstack/linaria and understand css variables:

creating bindings for it and create that bridge between CSS AST to the bindings.

Support [%styled] and [%styled ""]

Appears a weird error on the CSS_Parse apparently:

Parse error while reading token 'EOF'

Not entirely sure that it's the Parser itself, If you run Css_parse with an empty string works fine, maybe is the ppx detection that doesn't make it correctly.

Improve error messages

Since we use a custom lexer and pass down location everywhere we should be able to have a perfect error message.

  • Errors on wrong html tags
  • Errors on wrong css properties (css properties that doesn't exist)
  • Missing semicolon
  • Warn only support labelled arguments
  • Warn unused arguments
  • Warn unlabeled arguments
  • Make sure the location of errors are placed on the right property, and not the ppx loc
    Screenshot 2020-03-21 at 14 23 22
  • Can we re-message this? Error: This expression has type int but an expression was expected of type Emotion.Css.LengthPercentageAuto.t

Error Running Test

I have forked and run this repo. Built with esy command and then attempted to run esy test.

I got the following output in the terminal regarding the dune version:

➜  styled-ppx [master]esy test
File "/Users/prisc_000/.opam/ocaml-base-compiler.4.08.1/lib/js_of_ocaml-compiler/dune-package", line 1, characters 11-14:
1 | (lang dune 2.2)
               ^^^
Error: Version 2.2 of dune is not supported.
Supported versions:
- 0.0
- 1.0 to 1.11
➜  styled-ppx [master]

Is this expected or did i do something wrong?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.