Studiobear / designspek

A Reactive Design System: Svelte meets System UI
MIT License
8 stars 0 forks source link

Exploration: "utility-first" + inspiration/integration with Tailwind CSS #27

Closed samuelandert closed 4 years ago

samuelandert commented 4 years ago

Is your feature request related to a problem? Please describe. As a newby to the world of programming I find it quite hard to get into the abstraction of building a proper and nice designsystem from scratch. And as far I researched in svelte world there is a lack of good reference implementations.

Describe the solution you'd like I quite like the Blocks UI project which is sadly based on react. It would be great if there is just a very basic remake of Blocks in a Svelte REPL. I think for the POC just two different themes and two components, maybe a button with different variations and a header including the button component, are more than enough. (without even drag and drop).

Describe alternatives you've considered I played around with Svelte Styled System, but I don't know how to continue in order to customize and extend the theme and change the aliases (f.e. change the 'borderRadius' to the tailwind syntax 'rounded-' with a custom scale f.e. ["xs","md","lg","full","etc"]

Additional context I would like to bring the tailwind css syntax and theme specs including their nice aliases to the svelte world with the possibility to create and extend it into svelte native UI components. And then build a Blocks UI like design editor user interface to arrange and style the different components.

bearcanrun commented 4 years ago

The lack of opinionated design system in Svelte-land is very much why I started building Designspek. My inspiration and foundation was Svelte Styled System. I'm not a new JS developer, but this is my first complex library — abstraction — that intends to be useable by other people.

I think a tool like Blocks UI is beyond the scope of this library, but would be highly complimentary. At the minimum, a specification ("theme") generator is planned. Right now, I'm just stumbling through learning Svelte, applying this library to projects I'm working on and building a clearer understanding of where Designspek can/should go.

In comparison to Tailwind CSS, Designspek may have parallel practices, however right now its more of a CSS-in-JS solution rather than a style framework. Technically, the 'utility-first' styles of Tailwind can be used alongside Designspek. Or Designspek could create it's own utility-style objects to use in a similar manner as Tailwind.

I honestly haven't gotten that far in thinking. The latest struggles have been focused on finding a best practice for working with SSR and finding a way to pre-calculate styled values. This would mean that Designspek becomes a pre-processed CSS-in-JS solution — a best-of-both-worlds with development and flexibility benefits of CSS-in-JS without the production drawbacks normally inherent to such solutions.

samuelandert commented 4 years ago

I like your way of thinking and your CSS-in-JS approach and I understand that Blocks UI is beyond the scope of Designspek. I am quite willing to learn and help you as far as a I am capable of. What do you think about, as you suggested, if we create on top of Designspek our own utility first system, which is inspired by tailwind. If you help me getting started in how to implement it with some basic best practices, I happily do the foot work of adopting the tailwind syntax step by step and component by component.

bearcanrun commented 4 years ago

I am grateful for your continued thought and engagement. If I may coax you further in consideration:

  1. Have you used Tailwind directly with Svelte? What shortcomings or barriers have you hit that has you looking to other solutions?

  2. When building your apps or websites are there features you are specifically looking for in a styling system? Priority of those features? (e.g. rapid building, consistency, team-friendly, performance/optimization, etc)

  3. Have you used other CSS-in-JS solutions like for React (Themed-UI, Emotion, Styled Components, etc)?

In your OP, you talk about re-naming aliases, but to be clear, in Designspek these are just "shortcut" aliases that resolve directly to CSS properties. In JS, camelCase is used to write these properties, so most JS conversion to CSS looks like borderRadius --> border-radius. Shortcut aliases are simply for rapid-development through less typing:

{ m: 1, px:2 }

resolves to something like:

margin: 1rem; padding-left: 2rem; padding-right: 2rem;

"Classes" in Designspek are just objects of styles. There are natural shortcut aliases that act like utility classes, like the margin/padding shown above or for specific things likes colors declared in the theme (eg primary: '#012345'), but otherwise you'd declare your own style objects like:

export const Card = {
  d: 'block',
  pos: 'relative',
  zIndex: 1,
  bg: 'background', 
  boxShadow: '0px 3px 6px rgba(0, 0, 0, 0.16), 0px 3px 6px rgba(0, 0, 0, 0.23)',
  my: '2rem',
  mx: '0.5rem',
  p: '1rem 0.75rem'
}
\\ right now you have to use $theme.colors.background, but re-adding theme aliasing is high priority

You could create your own "utility" objects by declaring separate style objects and composing them. You can even keep them in a utilities-styles.js and import wherever needed:

export const containerSpace = {
  my: '2rem',
  mx: '0.5rem',
}

export const containerPad = {
  p: '1rem 0.75rem',
}

export const shadowed = {
  boxShadow: '0px 3px 6px rgba(0, 0, 0, 0.16), 0px 3px 6px rgba(0, 0, 0, 0.23)',
}

export const Card = {
  d: 'block',
  pos: 'relative',
  zIndex: 1,
  bg: 'background', 
  ...containerSpace
  ...containerPad
 ...shadowed
}

export CardHero = {
  ...Card,
  bg: 'primary',
  txtAlign: 'center'
}

In this way, I only need to create useful utility/pattern style objects as I need them. I'm both a graphic designer and programmer, so I usually generate my styles/themes in applications like Sketch, Figma, XD, etc. Then I code my style objects by page/view, layout, component, and extract common patterns/re-used styles (utility classes essentially) as needed. Defining my own utility objects means I pre-define consistency. I then do a final "optical" tweaking as things comes together.

A major consideration in Designspek is making those utility/patterns declarable in the theme. For a complex site, I end up have 20-30 at most, but everything else reasonably lives within their layout/element/component files. With JS, I don't care about class naming — it all renders out to something like '.go239232, .go123123' — which is half the battle when working directly with CSS/LESS/SASS, etc.

So yes, one could technically re-create Tailwind using Designspek, but consideration on value created versus just directly using Tailwind is recommended.

If I haven't chased you away and you still desire to discuss further or even collaborate, I'm quite happy to do so. Talking about all these facets facilitates further learning, understanding and clarity on my part!

samuelandert commented 4 years ago

Wow, thank you for the detailed response, very much appreciated as well. Let me quickly give you an introduction in what has triggered our passion to start working on our open source startup.

Our vision is to enable everyone to build up and quickly test any of their own ideas and platforms in as fast as possible rapid iteration steps, while featuring and committing to the open source paradigm for any industry.

Our mission is to offer an opinionated decentralized fullstack framework and platform builder with an maker friendly Userinterface and integrated easy drag and drop lego-like building blocks in order to enable everyone to build modern opensource multiplatform and resilient serverless (d)apps. A lot of our product inspiration comes from CODA, NodeRed, Figma, Storybooks and the GraphQLEditor.

Currently we are exploring various technologies by building many mini proof of concepts. Our first MVP is an offline first, decentralized and serverless encrypted by default database and schema editor build on ipfs and textile with an typesave ORM-Layer based on graphql with default realtime updates via subscriptions, including some reactive UI/UX sugar-syntax with svelte. So far we have build on top of textiles ThreadsDB a basic CRUD-GUI and our custom graphql adapter and tailwind css styling. We will launch this POC in a couple of days for others to play around with it.

For me this is the first serious developing project and I am learning a lot while just jumping into it.

Have you used Tailwind directly with Svelte? What shortcomings or barriers have you hit that has you looking to other solutions?

On this first POC I was using tailwind.css in a svelte project in order to get moving rapidly. I quite loved the utility first approach and the syntax of tailwind. After using it a while I stumbled upon the following shortcomings:

When building your apps or websites are there features you are specifically looking for in a styling system? Priority of those features? (e.g. rapid building, consistency, team-friendly, performance/optimization, etc)

I think the priority you mentioned is:

  1. RAPID BUILDING with Utility classes in order to get going
  2. CONSISTENCY while abstracting common building blocks into reusable styling components with clearly defined interfaces for its accepting parameters.
  3. TEAM FRIENDLY by abstracting components with clearly defined parameters a pattern library is emerging and thus its documentation is almost self-explanatory, with just looking at the component files.
  4. PERFORMANCE / OPTIMIZATION

In your OP, you talk about re-naming aliases, but to be clear, in Designspek these are just "shortcut" aliases that resolve directly to CSS properties. In JS, camelCase is used to write these properties, so most JS conversion to CSS looks like borderRadius --> border-radius. Shortcut aliases are simply for rapid-development through less typing:

Ok now I understood that and slowly I get my head around it. Thank you. Basically, designspek's shortcuts are just normal css and are not like tailwind utility classes, as they allow any css value to be ingested. As far as I understood you the benefits of tailwinds utility classes or in general system UI is consistency by design through restricting the possibility with scales for each design token.

{ m: 1, px:2 } resolves to something like:

How can I create my own shortcuts / alias?

You could create your own "utility" objects by declaring separate style objects and composing them. You can even keep them in a utilities-styles.js and import wherever needed:

How would I create for example a rounded "utility" object with the following scale: [xs, s, m, l, xl, 2xl etc] or margin and padding with [1,2,3,4,5,6,8,10,12,16], where the scale is f.e. [1=0.25rem, 2=0.5rem, 3 = 0.75rem etc]. I think in the ideal world applying utility classes would look something like the following, where the array will resolve into container based responsiveness rather than the media queries:

export const DIV = { rounded: 'xs', px: [2,4,6,10], py: [1,2,4,8], m: [2, none, 4], bg: green.500, text: primary, shadow: 'xl' }

bearcanrun commented 4 years ago

warning: I'm pushing back on your statements, but it is truly with all due respect and good intention. I've also guilty of herding this conversation into a more conceptual direction.

Our mission is to offer an opinionated decentralized fullstack framework and platform builder with an maker friendly Userinterface and integrated easy drag and drop lego-like building blocks in order to enable everyone to build modern opensource multiplatform and resilient serverless (d)apps.

From this context, your inquiry into Designspek is for the developer experience of yourself/your team. In contrast, your end-user doesn't care about classes and instead that they can build what they want, that it works, and is performant to the UX of their own end-users.

This is none of my business, but after near a decade jumping in and out of startups/crypto/decentralized/FOSS, etc, I can't help but caution you to keep focus on your mission and use what you know best despite any shortcomings. Avoid side-quest at all cost.

• I wanted to store all theming, all utilities, styles and component definitions in the database for easy plug in play reuse and remix for anyone and I didn't find a nice way of doing this with tailwind alone.

I'm not exactly sure what you're attempting, but it feels like there's multiple issues that you're trying solve but all rolled up into one solution. It might be better to break it apart and clarify what exactly you want in comparison to the intentions of the various libraries/frameworks you're looking at.


• tailwind is one big monolith which I felt was not easy to customize, especially integrating it into rollup and webpack is not that easy for beginners.

  1. RAPID BUILDING with Utility classes in order to get going

Build/bundling setup aside (though Google, et al shows plenty of people using Svelte + Tailwind), purpose of utility-classes-first approach and customization are at procedural odds with each other.


  1. CONSISTENCY while abstracting common building blocks into reusable styling components with clearly defined interfaces for its accepting parameters.

On Consistency: it is difficult because without strong scoping, specification and adoption, it washes out to subjectivity and burden.

In a parallel train of thought, the pursuit of ideals such as consistency without caution lead to over-engineering, over-abstraction, pre-optimization, and other development pitfalls.

In particular, I am pushing back on the concept of "container queries" as really just being pre-optimizations best reserved for as-needed/edge cases. Something like BlocksUI might very well be that edge case but once again, I argue careful consideration:

  1. Now that Microsoft has officially deprecated IE to be used for no more than migrational purposes, we have the blessing to support just "modern" browsers. This means a standard of device dimension state provided by media queries and responsive containers via the CSS Flex/Grid APIs. Using these browser-native interfaces covers at least, in a very conservative tone, 90% of the use cases that I come across. Going into that last 10% is when I might properly consider the use of native elements such as iframes. Specifically to something like Blocks UI or equivalent purposes like Storybook or Chrome dev tools, iframes properly encapsulate views for generated code that is most likely going to use a full device/browser views anyways.
  2. Adding container queries creates a multitude of new burdens:
    • Media queries are already considered a necessary evil. Some micro-management types have even tried controlling their layouts for every 50-100px increment change of device dimensions. But the inherent responsiveness of Flex/Grid to wrap/reposition is not just provided by screen width, but from "pressure" of their contents and properties to wrap/grow/shrink; thus allowing for fluid adaptation without micromanaging those incremental changes. Using these tools properly certainly is a mindset change from the fine-tooth control we programmers are used to wielding.
    • As media queries/responsive elements are native to the modern web browser environment, nothing needs to be added and they have optimization down to the binary level. Container queries have to rely on JS and attaching, monitoring and destroying event listeners and the added layer of abstraction, cpu/memory resources, etc attached to that.
    • Combining the two concerns above we have a the final questions of managing and maintaining container queries at scale. It's hard enough keeping 10s of device width straight, how do you manage 100s of components? I would have to see some solid demos to be convinced.

My personal conclusion is that the value created by container queries is much smaller than the cost to implement and maintain them. At best used only for edge-cases.

Related thoughts from your presented ideal application:

export const DIV = { rounded: 'xs', px: [2,4,6,10], py: [1,2,4,8], m: [2, none, 4], bg: green.500, text: primary, shadow: 'xl' }

  • In the big picture, this is one abstract view of how things ought to be done. If we were all so agreeable, the JS ecosystem would be much narrower. For an abstraction to survive, it needs adoptees to sustain it.
  • Context of "container size" vs "device screen size": When I see [1,2,3] and I know it relates to [320px, 768px, 1024px] and I go from style to style, the context remains the same. When that changes to the arbitrary size of a containers and uncertain relationship between their parent containers and how they respond, it becomes a much more difficult context to manage.
  • uses many types — defining with strings, numbers, arrays, objects and variables!
  • Potential issue — margin array only has 3 items (and extra-finnicky: has both string and number values) — when combined with the context item above, reasoning how things smoothly degrade, errors are handled, etc. there's a lot more coding to be created to handle the additional surface area.
  • The finicky-ness items could surely be smoothed by tooling, conventions, docs, etc. but keep in mind the added cost of reasoning, maintenance, adoption, coding the extra tooling, etc.

But to your questions:

How can I create my own shortcuts / alias? How would I create for example a rounded "utility" object with the following scale...

This level of flexibility/customizability isn't currently possible or planned. One of the attempts of this library was to try to keep it as small as possible while adding convenience of theming and CSS shortcuts. Svelte-Styled-System (which is inspired by but doesn't use Styled System) was a bare 1kb compressed. Extending to actually used Styled System plus a larger subset of the CSS API jumped Designspek to 8-9kb compressed. This is still less than the 13-16kb of Emotion/styled-components. As soon as I figure out the best way to parse styles and aliases with less loops and getting rid of Styled System, hopefully the library size will drop back down again and be more performant. This is technically one of the downsides of CSS-in-JS where you have cost of code to process styles + cost of style definitions. Once again, a balance of developer experience vs cost to user experience. Given the slimmer profile provided by the compilation nature of Svelte, it's an affordable cost from my perspective and a moot point if a preprocessing method is established.


• The way you build ui components with tailwind is by using @apply in a separate stylesheet, where (at least for me) it was not easy to integrate javascript variables. I much prefer your approach there, which gives a lot of flexibility, while maintaining consistency.

Is this true in Svelte? Given it's compiling nature I would think at worst you would have to concatenate strings or compose objects. The few example I saw using Tailwind certainly had component and styles together.

P.S. have you looked at the integration by Smelte | github

bearcanrun commented 4 years ago

I'm closing off this thought exploration for now in lieu of the the stance of Designspek taken in the Scope of Specifications issue #7 .

Thank you kindly for contributing to the thought cultivation of this library.

samuelandert commented 4 years ago

Alright, good talking to you, thank you very much too