hyf0 / pieces-js

(WIP)A library that transforms your regular CSS into Atomic CSS in compile time.
MIT License
2 stars 1 forks source link

dynamic css #2

Open jantimon opened 2 years ago

jantimon commented 2 years ago

does it support dynamic css?

e.g.:

css`
  transform: translateX(${x}px);
`
hyf0 commented 2 years ago

No. I haven't thought it through. But there's a workaround. dynamic styles

import React from 'react'
import { css } from '@pieces-js/tag'

export function Box({ size }) {
  return (
    <div
      className={css`
        height: var(--box-size);
        width: var(--box-size);
      `}
      style={{ '--box-size': size }}
    />
  )
}

The main goal, for now, is to solve the CSS rules order problem in atomic CSS-in-JS. You could find more details in this wonderful article Atomic CSS-in-JS.

After solving the order problem, the project will be production-ready and I will be focusing on adding tests.

jantimon commented 2 years ago

Dynamic styles are reducing the complexity of js-in-css a lot so it would be great to be build in without hard to maintain workarounds..

Also css-variables won't work in different cases e.g the following does not work:

@media (min-width var(--x)) { ... }

Afaik the order problem is really hard.

Which atomic styles should the following code generate and in which order?

.foo:nth-child(2n) {
  color: red;
}

.foo:active {
  color: blue;
}

.foo:not(:focus-within) {
  color: orange;
}

@media (min-width 300px) {
  .foo {
    color purple;
  }
}

and a second file:


@media (min-width 300px) {
  .bar {
    color purple;
  }
}

.bar:not(:focus-within) {
  color: orange;
}

.bar:active {
  color: blue;
}

.bar:nth-child(2n) {
  color: red;
}
hyf0 commented 2 years ago

Yeah. I'm going to remove At-rule and pseudo-classes supports. It creates a lot of edge cases. It will only support something like

const foo = css`
  margin: 0 20px;
  margin-top: 10px;
`
const bar = css`
  margin-top: 10px;
`

It first will be transformed to

const foo = css`
  margin-top: 0px;
  margin-bottom: 0px; 
  maring-left: 20px;
  maring-right: 20px;
  margin-top: 10px;
`

const bar = css`
  margin-top: 10px;
`

and finally

const foo = css`
  margin-bottom: 0px; 
  maring-left: 20px;
  maring-right: 20px;
  margin-top: 10px;
`

const bar = css`
  margin-top: 10px;
`

and compile to

const foo = 'c1 c2 c3 c4'

const bar = 'c4'

This way, no matter what the style order be,

.c1 { margin-bottom: 0px; }
.c2 { margin-left: 20px; }
.c3 { margin-top: 20px; }
.c4 { margin-top: 10px; }

and

.c4 { margin-top: 10px; }
.c3 { margin-top: 20px; }
.c2 { margin-left: 20px; }
.c1 { margin-bottom: 0px; }

will have a consistent result.

hyf0 commented 2 years ago

I haven't figured out how pseudo-classes fit into the process. but I guess At-rule supports will be removed forever.

hyf0 commented 2 years ago

@jantimon You are right that dynamic styles could reduce the complexity. But the approach of pieces-js requires static analysis, code like transform: translateX(${x}px); is very hard to do static analysis.

jantimon commented 2 years ago

what do you mean by removed?

no os based dark mode?

@media (prefers-color-scheme: dark) {

no responsiveness?

@media (max-width 1200px) {

no print?

@media print { 

no accessibility?

@media (prefers-reduced-motion) {

no element queries?

@container (min-width: 700px){

no touch detection?

@media (hover: none) and (pointer: coarse) {
hyf0 commented 2 years ago

Yes. That's the pain in this approach. Nicolas Gallagher has a talk about this. https://youtu.be/tFFn39lLO-U?t=1184

jantimon commented 2 years ago

oh there is also

@supports (display: grid) {
jantimon commented 2 years ago

so to summarize its css just with

to me this approach feels like it introduces a lot more problems than it is able to solve

hyf0 commented 2 years ago

yeah. But what do you mean about limited code splitting?

jantimon commented 2 years ago

let's say you have a special part of your application which is used rarely e.g. only on black friday which ships with special colors, fonts, font sizes and so on...

your code might look like this:

if (isBlackFriday) {
  import("./blackfriday");
}
if (isVipCustomer) {
  import("./vip");
}

this would create 3 chunks but if a specific order must be guaranteed I guess that's not possible anymore as you wouldn't know which css file is load first

this means that all users need to download the black friday and vip css the entire year

hyf0 commented 2 years ago

Yeah. I'm going to remove At-rule and pseudo-classes supports. It creates a lot of edge cases. It will only support something like

const foo = css`
  margin: 0 20px;
  margin-top: 10px;
`
const bar = css`
  margin-top: 10px;
`

It first will be transformed to

const foo = css`
  margin-top: 0px;
  margin-bottom: 0px; 
  maring-left: 20px;
  maring-right: 20px;
  margin-top: 10px;
`

const bar = css`
  margin-top: 10px;
`

and finally

const foo = css`
  margin-bottom: 0px; 
  maring-left: 20px;
  maring-right: 20px;
  margin-top: 10px;
`

const bar = css`
  margin-top: 10px;
`

and compile to

const foo = 'c1 c2 c3 c4'

const bar = 'c4'

This way, no matter what the style order be,

.c1 { margin-bottom: 0px; }
.c2 { margin-left: 20px; }
.c3 { margin-top: 20px; }
.c4 { margin-top: 10px; }

and

.c4 { margin-top: 10px; }
.c3 { margin-top: 20px; }
.c2 { margin-left: 20px; }
.c1 { margin-bottom: 0px; }

will have a consistent result.

It doesn't require specific order. The above example shows it.

jantimon commented 2 years ago

but pseudo elements need an order

.b {
  color: orange;
}
.c:hover {
  color:blue
}
.d:focus {
  color:purple
}

is not the same as

.d:focus {
  color:purple
}
.c:hover {
  color:blue
}
.b {
  color: orange;
}
hyf0 commented 2 years ago

yeah. Another downside :(.

jantimon commented 2 years ago

btw did you see https://compiledcssinjs.com/

hyf0 commented 2 years ago

First time saw this. Looks like it also has the order problem. https://codesandbox.io/s/musing-kilby-rwvcf?file=/pages/Foo.tsx

--- edit

Sorry. It seems doesn't have the order problem. It solves the problem using the way I described above.

It also supports At-rule, wondering how they do it.