vinpac / windstitch

Windstitch is a 1.4kB, Simple Styling Library that helps you set when a className should be applied to a component.
https://windstitch.vercel.app
MIT License
255 stars 6 forks source link

Feat: Support responsive variants #5

Open AbdallahAbis opened 2 years ago

AbdallahAbis commented 2 years ago

Would be great to support responsive variants, something like:

<ImageParentSkeletonComponent color='md:green lg:red' />

or

<ImageParentSkeletonComponent mdColor='green' lgColor='red' />
vinpac commented 2 years ago

Interesting case. Let me think about it and come with a proposal later today

AbdallahAbis commented 2 years ago

Sure, cause let's imagine you want a button's text to be flexible, sometimes you want it to be 2rem on mobile other times 1rem. There's no way to do that at the moment unless you pass conditional prop values using JavaScript.

AbdallahAbis commented 2 years ago

@vinpac Any updates on this?

vinpac commented 2 years ago

Hey @AbdallahAbis . Still thinking about this. The idea behind windstitch is to provide a way to control when classnames are applied to a component based solely on variants. Adding breakpoints would create a new layer of consideration when applying a classname.

Maybe we can abstract that from the main package into its own package. Like:

import { responsive } from '@windstich/responsive'

const variant = responsive({
  xs: 0,
  sm: 480,
  md: 700,
  lg: 1000
})

const Button = w.button('', {
  variants: {
    color: responsive({
      green: 'bg-green-500',
      red: 'bg-red-500'
    })
  }
})

function Home () {
  return <>
     <Button color="green" />
     <Button color={{ xs: 'green', sm: 'green', lg: 'red' }} />
  </>
}

The responsive function could look like this:

function responsive<Breakpoints extends Record<string, number>>(breakpoints: Breakpoints) {
  return function variant<Variants extends Record<string, string>>(variants: Variants) {
    return (value: keyof Variants | Record<keyof Breakpoints, keyof Variants>) => {
      // ... calculate the classnames based on the current breakpoint
    }
  }
}

One challenge here would be to update the classname when window size changes. This would require some watcher running.

What do you think?

AbdallahAbis commented 2 years ago

Looks great. is Windstitch responsible for calculating and generating styles? thought it only generates classNames and tailwind handles the rest?

vinpac commented 2 years ago

No no. Windstich is not responsible for calculating styles. It only applies classnames. The responsiveness on that would require a watcher.

That's my main concern because tailwind also handles responsive styles through their modifiers (e.g. md:bg-gray-500)

AbdallahAbis commented 2 years ago

I thought that you get the variant classNames and just add the responsive modifier to them:

import { responsive } from '@windstich/responsive'

const variant = responsive({
  xs: 0,
  sm: 480,
  md: 700,
  lg: 1000
})

const Button = w.button('', {
  variants: {
    color: responsive({
      green: 'bg-green-500',
      red: 'bg-red-500'
    })
  }
})

function Home () {
  return <>
     <Button color="green" />
     <Button color={{ xs: 'green', sm: 'green', lg: 'red' }} />
  </>
}

the above will result in:

  <>
     <button className="bg-green-500" />
     <button className="bg-green-500 lg:bg-red-500" />
  </>

Windstitch doesn't have to add custom breakpoints, instead it will just generate classNames and tailwind generated breakpoints and styles will be used?

vinpac commented 2 years ago

Unfortunately that woudln't work as tailwind statically extracts classnames from our code, so a dynamic string doesn't generate any output css.

image

To support what you're mentioning we would have to do:

const variants = {
  green: { xs: 'bg-green-500', sm: 'sm:bg-green-500', lg: 'lg:bg-green-500' },
  red: { xs: 'bg-red-500', sm: 'sm:bg-red-500', lg: 'lg:bg-red-500' } 
 }

And let windstitch apply it.

AbdallahAbis commented 2 years ago

Hmmm, I see. And how is the user going to pass those responsive variants to a component?

enyelsequeira commented 2 years ago

Hey @AbdallahAbis . Still thinking about this. The idea behind windstitch is to provide a way to control when classnames are applied to a component based solely on variants. Adding breakpoints would create a new layer of consideration when applying a classname.

Maybe we can abstract that from the main package into its own package. Like:

import { responsive } from '@windstich/responsive'

const variant = responsive({
  xs: 0,
  sm: 480,
  md: 700,
  lg: 1000
})

const Button = w.button('', {
  variants: {
    color: responsive({
      green: 'bg-green-500',
      red: 'bg-red-500'
    })
  }
})

function Home () {
  return <>
     <Button color="green" />
     <Button color={{ xs: 'green', sm: 'green', lg: 'red' }} />
  </>
}

The responsive function could look like this:

function responsive<Breakpoints extends Record<string, number>>(breakpoints: Breakpoints) {
  return function variant<Variants extends Record<string, string>>(variants: Variants) {
    return (value: keyof Variants | Record<keyof Breakpoints, keyof Variants>) => {
      // ... calculate the classnames based on the current breakpoint
    }
  }
}

One challenge here would be to update the classname when window size changes. This would require some watcher running.

What do you think?

Hey @vinpac is this the way to handle responsiveness? and is it added in the library already? I am also looking how it would work handling responsiveness

vinpac commented 2 years ago

Hey @enyelsequeira . This is still not added to the library. I will work on it as soon as possible. I was working on a conflicting classnames which have just being published as a Pull Request

jpmaga commented 1 year ago

Hey there @vinpac, have you managed to get anywhere with this? We have an in-house solution very similar to windstitch, and we managed to get responsive variants using more or less the same you mentioned, using window.matchMedia and a watcher subsequently. Which works just fine on client-side, but it doesn't work so well when the page is rendered on the server, as expected. I would also prefer not to create responsive classes for every variant every time, if possible. So, I'm fresh out of ideas. :D

(we don't use tailwind often, though)

The dynamic variants, however, seem to work fine, haven't tested them much, but basically it uses css variables. if the prop is not an object it will use the style attribute to define it, otherwise it will add a style tag just above the component with the media query and the values. I don't see this being useful to windstitch, given it only cares for classnames but just throwing it out there, maybe you can make something with the idea.