vinpac/windstitch

Feat: Support responsive variants

AbdallahAbis opened this issue · 12 comments

Would be great to support responsive variants, something like:

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

or

<ImageParentSkeletonComponent mdColor='green' lgColor='red' />

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

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.

@vinpac Any updates on this?

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?

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

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)

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?

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.

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

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

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

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.