components – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 26 Apr 2022 15:26:51 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 components – CSS-Tricks https://css-tricks.com 32 32 45537868 Avoiding the Pitfalls of Nested Components in a Design System https://css-tricks.com/nested-components-in-a-design-system/ https://css-tricks.com/nested-components-in-a-design-system/#comments Tue, 26 Apr 2022 14:30:20 +0000 https://css-tricks.com/?p=365359 When creating a component-based, front-end infrastructure, one of the biggest pain points I’ve personally encountered is making components that are both reusable and responsive when there are nested components within components.

Take the following “call to action” (<CTA />


Avoiding the Pitfalls of Nested Components in a Design System originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
When creating a component-based, front-end infrastructure, one of the biggest pain points I’ve personally encountered is making components that are both reusable and responsive when there are nested components within components.

Take the following “call to action” (<CTA />) component, for example:

On smaller devices we want it to look like this:

This is simple enough with basic media queries. If we’re using flexbox, a media query can change the flex direction and makes the button go the full width. But we run into a problem when we start nesting other components in there. For example, say we’re using a component for the button and it already has a prop that makes it full-width. We are actually duplicating the button’s styling when applying a media query to the parent component. The nested button is already capable of handling it!

This is a small example and it wouldn’t be that bad of a problem, but for other scenarios it could cause a lot of duplicated code to replicate the styling. What if in the future we wanted to change something about how full-width buttons are styled? We’d need to go through and change it in all these different places. We should be able to change it in the button component and have that update everywhere.

Wouldn’t it be nice if we could move away from media queries and have more control of the styling? We should be using a component’s existing props and be able to pass different values based on the screen width.

Well, I have a way to do that and will show you how I did it.

I am aware that container queries can solve a lot of these issues, but it’s still in early days and doesn’t solve the issue with passing a variety of props based on screen width.

Tracking the window width

First, we need to track the current width of the page and set a breakpoint. This can be done with any front-end framework, but I’m using a Vue composable here as to demonstrate the idea:

// composables/useBreakpoints.js

import { readonly, ref } from "vue";

const bps = ref({ xs: 0, sm: 1, md: 2, lg: 3, xl: 4 })
const currentBreakpoint = ref(bps.xl);

export default () => {
  const updateBreakpoint = () => {
  
    const windowWidth = window.innerWidth;
    
    if(windowWidth >= 1200) {
      currentBreakpoint.value = bps.xl
    } else if(windowWidth >= 992) {
      currentBreakpoint.value = bps.lg
    } else if(windowWidth >= 768) {
      currentBreakpoint.value = bps.md
    } else if(windowWidth >= 576) {
      currentBreakpoint.value = bps.sm
    } else {
      currentBreakpoint.value = bps.xs
    }
  }

  return {
    currentBreakpoint: readonly(currentBreakpoint),
    bps: readonly(bps),
    updateBreakpoint,
  };
};

The reason we are using numbers for the currentBreakpoint object will become clear later.

Now we can listen for window resize events and update the current breakpoint using the composable in the main App.vue file:

// App.vue

<script>
import useBreakpoints from "@/composables/useBreakpoints";
import { onMounted, onUnmounted } from 'vue'

export default {
  name: 'App',
  
  setup() {
    const { updateBreakpoint } = useBreakpoints()

    onMounted(() => {
      updateBreakpoint();
      window.addEventListener('resize', updateBreakpoint)
    })

    onUnmounted(() => {
      window.removeEventListener('resize', updateBreakpoint)
    })
  }
}
</script>

We probably want this to be debounced, but I’m keeping things simple for brevity.

Styling components

We can update the <CTA /> component to accept a new prop for how it should be styled:

// CTA.vue
props: {
  displayMode: {
    type: String,
    default: "default"
  }
}

The naming here is totally arbitrary. You can use whatever names you’d like for each of the component modes.

We can then use this prop to change the mode based on the current breakpoint:

<CTA :display-mode="currentBreakpoint > bps.md ? 'default' : 'compact'" />

You can see now why we’re using a number to represent the current breakpoint — it’s so the correct mode can be applied to all breakpoints below or above a certain number.

We can then use this in the CTA component to style according to the mode passed through:

// components/CTA.vue

<template>
  <div class="cta" :class="displayMode">
    
    <div class="cta-content">
      <h5>title</h5>
      <p>description</p>
    </div>
    
    <Btn :block="displayMode === 'compact'">Continue</Btn>
    
  </div>
</template>

<script>
import Btn from "@/components/ui/Btn";
export default {
  name: "CTA",
  components: { Btn },
  props: {
    displayMode: {
      type: String,
      default: "default"
    },
  }
}
</script>

<style scoped lang="scss">
.cta {
  display: flex;
  align-items: center;
  
  .cta-content {
    margin-right: 2rem;
  }

  &.compact {
    flex-direction: column;
    .cta-content {
      margin-right: 0;
      margin-bottom: 2rem;
    }
  }
}
</style>

Already, we have removed the need for media queries! You can see this in action on a demo page I created.

Admittedly, this may seem like a lengthy process for something so simple. But when applied to multiple components, this approach can massively improve the consistency and stability of the UI while reducing the total amount of code we need to write. This way of using JavaScript and CSS classes to control the responsive styling also has another benefit…

Extensible functionality for nested components

There have been scenarios where I’ve needed to revert back to a previous breakpoint for a component. For example, if it takes up 50% of the screen, I want it displayed in the small mode. But at a certain screen size, it becomes full-width. In other words, the mode should change one way or the other when there’s a resize event.

Showing three versions of a call-to-action components with nested components within it.

I’ve also been in situations where the same component is used in different modes on different pages. This isn’t something that frameworks like Bootstrap and Tailwind can do, and using media queries to pull it off would be a nightmare. (You can still use those frameworks using this technique, just without the need for the responsive classes they provide.)

We could use a media query that only applies to middle sized screens, but this doesn’t solve the issue with varying props based on screen width. Thankfully, the approach we’re covering can solve that. We can modify the previous code to allow for a custom mode per breakpoint by passing it through an array, with the first item in the array being the smallest screen size.

<CTA :custom-mode="['compact', 'default', 'compact']" />

First, let’s update the props that the <CTA /> component can accept:

props: {
  displayMode: {
    type: String,
    default: "default"
  },
  customMode: {
    type: [Boolean, Array],
    default: false
  },
}

We can then add the following to generate to correct mode:

import { computed } from "vue";
import useBreakpoints from "@/composables/useBreakpoints";

// ...

setup(props) {

  const { currentBreakpoint } = useBreakpoints()

  const mode = computed(() => {
    if(props.customMode) {
      return props.customMode[currentBreakpoint.value] ?? props.displayMode
    }
    return props.displayMode
  })

  return { mode }
},

This is taking the mode from the array based on the current breakpoint, and defaults to the displayMode if one isn’t found. Then we can use mode instead to style the component.

Extraction for reusability

Many of these methods can be extracted into additional composables and mixins that can be reuseD with other components.

Extracting computed mode

The logic for returning the correct mode can be extracted into a composable:

// composables/useResponsive.js

import { computed } from "vue";
import useBreakpoints from "@/composables/useBreakpoints";

export const useResponsive = (props) => {

  const { currentBreakpoint } = useBreakpoints()

  const mode = computed(() => {
    if(props.customMode) {
      return props.customMode[currentBreakpoint.value] ?? props.displayMode
    }
    return props.displayMode
  })

  return { mode }
}

Extracting props

In Vue 2, we could repeat props was by using mixins, but there are noticeable drawbacks. Vue 3 allows us to merge these with other props using the same composable. There’s a small caveat with this, as IDEs seem unable to recognize props for autocompletion using this method. If this is too annoying, you can use a mixin instead.

Optionally, we can also pass custom validation to make sure we’re using the modes only available to each component, where the first value passed through to the validator is the default.

// composables/useResponsive.js

// ...

export const withResponsiveProps = (validation, props) => {
  return {
    displayMode: {
      type: String,
      default: validation[0],
      validator: function (value) {
        return validation.indexOf(value) !== -1
      }
    },
    customMode: {
      type: [Boolean, Array],
      default: false,
      validator: function (value) {
        return value ? value.every(mode => validation.includes(mode)) : true
      }
    },
    ...props
  }
}

Now let’s move the logic out and import these instead:

// components/CTA.vue

import Btn from "@/components/ui/Btn";
import { useResponsive, withResponsiveProps } from "@/composables/useResponsive";

export default {
  name: "CTA",
  components: { Btn },
  props: withResponsiveProps(['default 'compact'], {
    extraPropExample: {
      type: String,
    },
  }),
  
  setup(props) {
    const { mode } = useResponsive(props)
    return { mode }
  }
}

Conclusion

Creating a design system of reusable and responsive components is challenging and prone to inconsistencies. Plus, we saw how easy it is to wind up with a load of duplicated code. There’s a fine balance when it comes to creating components that not only work in many contexts, but play well with other components when they’re combined.

I’m sure you’ve come across this sort of situation in your own work. Using these methods can reduce the problem and hopefully make the UI more stable, reusable, maintainable, and easy to use.


Avoiding the Pitfalls of Nested Components in a Design System originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/nested-components-in-a-design-system/feed/ 16 365359
How Do You Handle Component Spacing in a Design System? https://css-tricks.com/component-spacing-design-system/ https://css-tricks.com/component-spacing-design-system/#comments Tue, 25 Jan 2022 23:10:20 +0000 https://css-tricks.com/?p=362044 Say you’ve got a <Card /> component. It’s highly likely it shouldn’t be butted right up against any other components with no spacing around it. That’s true for… pretty much every component. So, how do you handle component spacing in …


How Do You Handle Component Spacing in a Design System? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Say you’ve got a <Card /> component. It’s highly likely it shouldn’t be butted right up against any other components with no spacing around it. That’s true for… pretty much every component. So, how do you handle component spacing in a design system?

Do you apply spacing using margin directly on the <Card />? Perhaps margin-block-end: 1rem; margin-inline-end: 1rem; so it pushes away from the two sides where more content natural flows? That’s a little presumptuous. Perhaps the cards are children inside a <Grid /> component and the grid applies a gap: 1rem. That’s awkward, as now the <Card /> component spacing is going to conflict with the <Grid /> component spacing, which is very likely not what you want, not to mention the amount of space is hard coded.

Example of a component spacing where a card component is to the left of an accordion component and above an article, with 50 pixels of spacing between all three elements. Lorem i-sum text throughout in a mono font. The card has a Calvin and Hobbes comic image.
Adding space to the inline start and block end of a card component.

Different perspectives on component spacing

Eric Bailey got into this recently and looked at some options:

  • You could bake spacing into every component and try to be as clever as you can about it. (But that’s pretty limiting.)
  • You could pass in component spacing, like <Card space="xxl" />. (That can be a good approach, likely needs more than one prop, maybe even one for each direction, which is quite verbose.)
  • You could use no component spacing and create something like a <Spacer /> or <Layout /> component specifically for spacing between components. (It breaks up the job of components nicely, but can also be verbose and add unnecessary DOM weight.)

This conversation has a wide spectrum of viewpoints, some as extreme as Max Stoiber saying just never use margin ever at all. That’s a little dogmatic for me, but I like that it’s trying to rethink things. I do like the idea of taking the job of spacing and layout away from components themselves — like, for example, those content components should completely not care where they are used and let layout happen a level up from them.

Adam Argyle predicted a few years back that the use of margin in CSS would decline as the use of gap rises. He’s probably going to end up right about this, especially now that flexbox has gap and that developers have an appetite these days to use CSS Flexbox and Grid on nearly everything at both a macro and micro level.


How Do You Handle Component Spacing in a Design System? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/component-spacing-design-system/feed/ 7 362044
What Were the Hottest Front-End Tools in 2021? https://css-tricks.com/hottest-front-end-tools-in-2021/ https://css-tricks.com/hottest-front-end-tools-in-2021/#comments Mon, 17 Jan 2022 15:13:26 +0000 https://css-tricks.com/?p=361047 Another year has passed and once again I’ve had the privilege of going through the Web Tools Weekly newsletter archives from the past 12 months to hunt down the front-end tools that readers found to be the most interesting during …


What Were the Hottest Front-End Tools in 2021? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Another year has passed and once again I’ve had the privilege of going through the Web Tools Weekly newsletter archives from the past 12 months to hunt down the front-end tools that readers found to be the most interesting during 2021. So, to kick off 2022, I’ve compiled a list of the 60 most popular tools. I’m sure there are at least one or two listed here that you can start using in your front-end projects today.

Some of these front-end tools are super-practical, while others probably only made this list due to curiosity (which I base on the number of unique clicks). And since many of the tools that make my year-end lists are pretty new, I think this is a good indicator of the kinds of front-end tools that will be popular in the coming year.

Counting down from the top!

Table of contents


60. Open Props

Open Props provides a set of hand-crafted design tokens made up of CSS custom properties. I can see this sort of thing being much more common due to the use of this particular CSS feature. These allow you to drop in small collections of useful groups of custom properties, like animations.css, borders.css, fonts.css, zindex.css, etc, with more coming soon. Or you can just use the main Open Props file to grab everything at once.

Screenshot of the Open Props homepage which outlines three things that make it a useful front-end tool, including design tokens, consistent components, and useful in any framework.

This is definitely one front-end tool to keep an eye on and maybe even a good one to contribute to if you want to help build the library of tokens available.

59. NextUI

A modern React library that uses Stitches, a popular CSS-in-JS solution, and includes light and dark UI components out-of-the-box along with a default color palette that might be good for quickly building landing pages or other content that’s not initially tied to any branding.

It’s currently listed as being in Alpha stage of development, so this is another one worth keeping tabs on in the coming year.

58. Dopefolio

If you’re looking for an easy way to put together your own developer portfolio, Dopefolio is a quick solution that’s optimized for SEO and has strong Lighthouse scores out-of-the-box.

The live demo gives you an idea of what it looks like (responsive and all) and it includes a color picker component so you can live-test your own preferred primary color for the template.

57. Vizzu

This is a unique one that made this year’s list of front-end tools. Vizzu an open-source JavaScript library for creating animated data stories and visualizations. Maybe this is popular due the apparent need for creating and embedding medical data nowadays.

This library allows you to easily build static data charts, animated charts, and data stories. You can see some live examples of the animated charts or data stories, which include a data story showing which guest character had the most lines throughout the run of the TV series Friends.

56. 10015 Tools

I seem to find a handful of these types of front-end tools every year. 10015 Tools is a collection of front-end tools rather than one single tool. It includes text tools, image tools, CSS tools, coding tools (e.g. minifiers), color tools, social media tools, and a few others under a miscellaneous category.

This is definitely one of the more comprehensive all-in-one solutions I’ve seen, so I’m sure you’ll find a few useful front-end tools here you can come back to.

55. Snoweb

Icon sets are always popular and I come across at least a dozen new ones every year. This one includes optimized SVG icons, many of which have a built-in animated effect when you hover over the icon (like the open/close envelope icon).

Like many icon websites, you can search by keyword or filter by category, one of which is a “brand” category with icons for Twitter, Facebook, Vimeo, YouTube, Snapchat, etc.

54. Tails

Here’s the first Tailwind-based tool to make the list; something that’s been a trend the past few years as Tailwind continues to grow in popularity. This front-end tool a drag-and-drop page builder for projects using Tailwind CSS.

It includes two free “blocks” for each of the 15 categories, so there’s a decent amount of free stuff here and you can unlock the rest for a monthly fee.

53. MapLibre

This open-source mapping library launched in March and has grown in popularity throughout the year. It includes a JavaScript library as well as an SDK for displaying maps inside of iOS and Android apps.

The docs include a bunch of examples that use JavaScript, if you want to see what’s possible with the web-based front-end tools.

52. SVG Repo

SVG Repo makes the list based on sheer numbers alone. It’s a repository of more than 300,000 free, optimized, SVG-based graphics and icons most of which are licensed for commercial use.

Each icon you select tells you what type of license it falls under. You can search by keyword and “save” icons to your favorites for later use (no login required).

51. Animated Backgrounds

This one made the top 60 this year but probably isn’t the most practical front-end tool of the bunch. Like I said at the outset, some stuff gets clicked on just out of sheer curiosity. It’s a gallery of animated backgrounds, sourced from various CodePen demos.

The gallery is useful in that it displays the backgrounds as pre-recorded videos so you don’t have to worry about all the demos loading and crashing your browser tab. Use these sparingly, if ever, as they often use heavy amounts of JavaScript and CSS.

50. Pico.css

This is a different type of CSS framework compared to what’s popular nowadays. It’s kind of like an anti-Tailwind tool and more of a starter CSS file (like a reset) than a full-fledged framework.

Pico.css provides elegant default styles on all native HTML elements (which you can preview here) without the need to add swaths of classes to your markup — and the whole thing is less than 10kb minified and gzipped.

49. Coding Fonts

Coding fonts seem to be an exciting new trend of late, and CSS-Tricks has taken advantage of that with this little interactive info app.

Select a font on the left to display example code, typeface info (ligatures, italics, etc.), cost (most are free), along with options to display example code for HTML, CSS, or JavaScript.

48. UI-Neumorphism

This React library is based on the old skeumorphism trend that apparently went out of style a number of years ago. Is this trend making a comeback? Probably not. In fact, this React library is about two years old even though I shared it for the first time this past year.

The library includes a slew of different components that all implement “neumorphism” look. Interesting to look at, but might not fit many projects.

47. Beautiful CSS Buttons

Just about every app or website needs buttons, so a collection like this always seems to do well. Many of these are different from what you probably have seen elsewhere.

A lot of the buttons include interesting hover effects and you can easily copy and paste the HTML and CSS for any single button directly on the page.

46. Shaper

This interactive tool lets you fiddle with various UI settings live on the page to build different page elements. Once you’ve tweaked things as you like, you can grab the code, which uses CSS custom properties.

You can switch between demo view and “specs” view (i.e. the code along with other useful info). This tool has a particular focus on typography along with the rest of the UI styles, which is an important part of getting a layout looking right.

45. Prestige

This is a text-based HTTP client in the browser — like Postman but without an interface. It allows you to define requests in plain text, which you can optionally save as a Gist. It includes isolated cookie management, and has both dark and light modes.

As the author explains, “I built Prestige because I needed an app like this when working […] and playing with external APIs.”

44. HTML.cafe

This is probably the simplest tool in the entire list. HTML.cafe is more or less a poor-man’s CodePen, for lack of a better term. I wouldn’t think people would be interested in this sort of thing, since there are already powerful tools that already do what it does. Nonetheless, it’s a really simple HTML editor with live preview.

There are no separate CSS or JavaScript windows like there are CodePen, but you can easily include those in <style> or <script> elements if you wish. I think the best use of this tool might be for teachers helping students who are completely unfamiliar with HTML, as this gets all the complexities out of the way.

43. Charts.css

This front-end tool is sort of like a cross between Tailwind CSS and Chart.js. In short, Charts.css is a CSS framework that lets you use utility classes to build charts using HTML and CSS.

You can create bar charts, line charts, multi-dataset charts, percentage columns, and 3D bar charts. And best of all, the charts are accessible, responsive, and easy to customize to your branding needs.

42. Buttons Generator

“Buttons Generator” is probably a bit of a misnomer, since the page doesn’t exactly let you “generate” buttons. Like the previous buttons resource, this is a gallery of buttons built with HTML and CSS.

They’re divided up by category, include some neat hover or click effects, and you can click any button to copy the code to your clipboard.

41. Doodad Pattern Generator

This interactive tool allows you to build your own patterned backgrounds that you can export in a variety of formats. You can use the “shuffle” button to generate a random pattern, or select from various category styles. You can also edit colors, filters, and various transforms.

The export dialog lets you save patterns and load previously-saved ones via Local Storage. Export your patterns as JPEG, PNG, inline SVG, SVG file, or CSS background.

40. Kaboom

Every year I come across at least one or two new JavaScript game libraries, and this is the one that made this year’s list. It looks to have a fairly elegant and easy-to-use API and includes a healthy set of components, events, and other built-in functions.

There’s a handy playground to get your feet wet with using it and an introductory tutorial to get started.

39. Skuawk

If you want an alternative to Unsplash, which is likely one of your go-to sources for free stock images, this collection of beautiful public domain images might be a good option with some gorgeous photos.

There are 16 categories of images from various photographers that have all allowed their images to be used under a CC0 license (i.e. do whatever you want).

38. Glassmorphism CSS Generator

This is an online generator that lets you build a “glassmorphism” effect on a page element — kind of like frosted glass. This tool is apparently supposed to be part of a larger UI library that will incorporate this sort of effect on a number of different UI components.

Whatever the case, I like this effect more than the “neumorphism” one and apparently my audience agrees.

37. Kalia

This is one of three VS Code tools that made the list. It’s an attractive color scheme you can use for your VS Code setup.

The extension lists only about 800 installs so far, which is surprising, but it has a nice pastel color look that I think many will enjoy.

36. AdminJS

If you’re a Node developer, this is an open-source admin panel that can be added to a Node.js app. It will generate a UI for you, based on data you’ve added from almost any database, allowing you and your team to manage your app’s content.

You can try it out using this example app, which is based on MongoDB and Postgres.

35. Pancake

Here’s another game engine to build cross-platform HTML5-based 2D games. This one got a lot of traction when I first shared it. While it made the list,it seems to require Python during the build step after you write the game.

The basic API uses plain JavaScript and you can view lots of neat little examples here.

34. Mosaic Lite

I find lots of dashboard templates built with different front-end technologies. This one is made with Tailwind and React and includes optional chart components built with Chart.js.

Like other similar templates, this can be used for SaaS products, admin dashboards, and more. You can view a live demo.

33. Iconduck

Here’s another great source for open-source icons, this one offering well over 100,000 icons that are searchable by keyword and are all available for use in commercial projects.

The site includes ability to like and save icons and icon collections for later use (requires cookies, but no login).

32. Luxa CSS

This is a CSS library that was actually released in mid-to late 2020, but I shared it for the first time in 2021. It’s described as a “minimalist” CSS framework.

Luxa CSS includes some base styles, along with various components, helpers, and layout styles, which you can view in the docs or by checking out this CodePen collection.

31. Glitter

Glitter was definitely one of the strangest — yet coolest — front-end tools I came across over the past year and it seems to have drawn a lot of interest even though it’s as simple as any tool gets.

It’s a generator that produces text in a glitter-like style, which you can save as SVG. Definitely not for 99% of your projects, but a pretty cool text effect.

30. Components AI

When I originally shared this one, I was sharing the theme builder alone, which is what got it on this list. But it’s worth sharing the entire set of 15+ tools.

In addition to the theme builder, there’s a syntax highlighter builder, gradient and shadow tools, SVG pattern generators, animated backgrounds, and lots more.

29. Unicode Arrows

This is pretty straightforward. Unicode Arrows a one-stop location to copy and paste — you guessed it — Unicode arrows along with each arrow’s associated hex code.

Unicode Arrows

Not much else to say about this one except that the site lets you buy Unicode arrow jewellery. Not that a bunch of coding nerds would be interested in that, no way.

28. Type Scale Clamp Generator

This is not the first tool to attempt to generate a type scale for you, but it’s a relatively new one that incorporates CSS’s clamp() function.

The front-end tool allows you to select a range, font, preview text, and you can even test the responsiveness (though I don’t see how useful that latter feature is, considering this is just text).

27. AnimXYZ

This one is described as “the first composable CSS animation toolkit” with support for Vue and React. What that means exactly is that you don’t have to write any keyframes. It seems to be kind of like Tailwind for animations, since you’re only using HTML classes.

In addition to adding classes, the values are built entirely using CSS variables. That means you can customize the values as you wish by modifying the variables themselves.

26. Frontend Toolkit

Here’s another all-in-one tools solution that includes more than 20 tools for doing various coding and image-related tasks.

It includes tools for CSS, JSON, favicons, SVG, image compression, npm, regex, and more.

25. colorpalettes.earth

Here’s one that’s unique in the list and may inspire some cool designs. This tool displays color palettes sourced from images of nature (taken from Unsplash) that are included on the site, with new palettes added regularly.

Click any image and you’ll get a modal with access to the hex value for each of the colors that make up the image-derived palette.

24. Uncut

Adding to the diversity of this list, here’s a typeface catalogue that currently features 90 typefaces with a focus on contemporary, or modern, type.

All fonts included are open-source, so you’re free to use them in personal and commercial projects.

23. Lowdefy

Building internal tools seems to be a hot thing nowadays and this is one solution you might want to look into that lets you build your tools by writing YAML.

It’s described as an “open-source low-code framework to build web apps, admin panels, BI dashboards, workflows, and CRUD apps with ease.”

22. JavaScript Booster

This is a VS Code extension that aims to help you, as the it says on the tin, boost your JavaScript, TypeScript, and React coding productivity. The extension adds a light bulb icon at certain points in your code, indicating that you can instantly trigger predefined code refactorings.

Some examples include converting a regular function to an arrow function, flip an if-else construct, along with some React-specific refactorings.

21. Layout Patterns

This is one of the most recent additions to Google Developers’ web.dev resource that I’m assuming will continue to grow in the coming year.

It includes a number of UI patterns “built using modern CSS APIs.” In other words, it’s a very forward-thinking collection of CSS examples, but should be used with caution since some of the technologies incorporated may not have full browser support yet.

20. Baseline Background Remover

Admittedly, AI-based background remover tools have been a dime-a-dozen recently. This one is free and works really well from my brief testing with it.

You can upload an image of up to 5MB and it will accept JPEG and PNG files. The resulting image is downloaded a transparent PNG, which you can use to add your own background or leave as transparent.

19. Theatre.js

Here’s another animation library, but this time a JavaScript solution that allows you to animate DOM elements or WebGL using a convenient visual editor that works along with the code you write.

This is a really powerful tool that’s hard to encapsulate in just a few paragraphs. There’s a lenghty chapter-based video used throughout the docs that really helps if you want to get familiar with it.

18. Transition.css

Drop-in CSS libraries are always popular and I’ll usually find at least one or two good ones each year. This one includes some neat CSS transitions you probably haven’t seen elsewhere.

You can try them out right on the page. My favourites are the ones that incorporate some hesitation in the animation, for an added uniqueness.

This is sort of a catch-all for design systems as it features component examples sourced from real design systems built in various technologies — React, CSS, Angular, Vue, etc. — by various brands, including eBay, Goldman Sachs, GOV.UK, and lots more.

It works as a handy reference for anyone building their own design system, as you can compare the same components across the existing systems included here.

16. party.js

This is a fun and unique JavaScript library that lets you add particle effects to a web page, specifically confetti and sparkles.

You’ll only use this in very specific circumstances, but it’s nice that you can customize the particle shapes, number of particles, spread, and so on.

15. Headless UI

In the words of Nacho Libre, now we’re really getting down to the nitty gritty. This UI component library was released in late 2020 and has already amassed more than 12,000 stars on GitHub.

The components (dropdown menu, tabs, popover, etc.) are “headless.” That doesn’t mean their capa was detated; it means they’re unstyled so that you can brand them as you please. They’re also fully accessible, designed to integrate with Tailwind CSS, and are compatible with React and Vue. That’s right — this one hits almost every front-end buzzword for 2022.

14. Turbo

This is another one that did well throughout 2021 after a late 2020 release. It’s billed as “the speed of a single-page web application without having to write any JavaScript.”

In brief, Turbo is a library that lets you take advantage of four main features: Turbo Drive, Turbo Frames, Turbo Streams, and Turbo Native. These use web components to add single-page app-like performance and interactivity to your pages without the need to reinvent the wheel with heavy custom scripts.

13. tidy.js

This is a library of data-related JavaScript functions specifically for “tidying up” your data.

It includes 70+ functions under different categories (tidying, grouping, math, sequencing, etc.) and you can mess around with the different features using this playground.

12. Tail-Kit

This is the first Tailwind UI kit on the list of top front-end tools, and it’s a doozy. It has more than 250 open-source components that are compatible with React, Vue, and Angular.

There are components categorized under Elements, Forms, Commerce, Navigation, Sections, and Lists, or you can use from a number of templates, categorized under Dashboards, Landing Pages, and Error Pages.

11. Tailwind Components

And here’s another Tailwind UI kit, again featuring open-source components and templates under 13 more refined categories, along with an “awesome” category that includes free but premium components.

This site is more or less a directory of various community-contributed Tailwind components, rather than a cohesive set of UI elements like other kits.

10. Pikaday

A JavaScript date picker cracks the top 10 tools of the year in 2021 — who would have thunk it? It seems to check all the boxes necessary for a date picker component: No dependencies, lightweight, and uses modular CSS for styling.

Like a few other front-end tools on this list, this isn’t a new tool. It’s been around for a while, but I first shared it in 2021 and it amazingly ended up in this year’s top 10.

9. HTML Boilerplates

This is a practical little online HTML generator that lets you customize the type of HTML starter template you want to generate, providing toggle options for what to include. This is probably most useful for generating a quick template for a landing page or demo.

In all honesty, I think it would be good if the tool was updated to use a few more modern options, but for a simple HTML starting point this gets the job done.

8. Whirl

Here’s another CSS animation library, this time specifically a collection of animations for use as loading spinners. It includes 100+ animated loaders, some of which are really neat and unique.

I like how the animations in the list (which you can try right on the page) are categorized as pseudo-element, single element, and multi-element. The best one by far is the “pong” animation, though I question whether anyone would correctly classify that as indicative of “content loading”!

7. Riju

Imagine if CodePen and JSFiddle had a baby, then you chopped that baby into 224 pieces. That’s what Riju is — a fast online playground for just about every programming language.

I can’t imagine there’s any coding language you’d want to play around with that’s missing here and most of it is stuff that you don’t normally associate with running in the browser.

6. DevUI

This is an Angular toolkit that’s suitable for enterprise-level apps and includes components, icons, an admin dashboard template, and a design system for styling and branding.

I’m guessing this made the top 10 list of front-end tools because I didn’t specifically mention at first that it is for Angular apps (which isn’t clear on the home page either). Nonetheless, I did specify that it’s for enterprise-level projects, and that seemed to grab the attention of many.

5. Pollen

This library works as a foundation for your own design system, and its practicality is evident in its use of CSS custom properties.

It provides you with low-level design tokens that you can easily customize and extend. Modules include Typography, Layout, UI, Grid, and Colors. I’m guessing there will be more added to this, so it’s one to keep an eye on in the coming year.

4. AlterNight

Developers love VS Code, dark mode, and plugins. Combine those three things, and you have a great little front-end tool.

AlterNight is a beautiful VS Code theme and has a modest ~3,000 installs so far, but it was able to crack the top five in this year’s list.

3. UIsual

Here’s a collection of front-end templates, but with a bit of a twist that I think many seemed to appreciate: they’re greyscale.

The set currently includes eight templates with diverse layouts. With the lack of any color-based branding, these are a good option to customize to your own needs without looking like every other landing page out there.

2. Supabase UI

Here’s yet another open-source component library, this one for React and designed specifically for the Supabase product (an open-source Firebase alternative).

It’s Tailwind-ready and I should also point out that it’s still in early development. Nonetheless, it was popular enough to make it to number 2 on this list.

1. CSS Layout Generator

This was the most-clicked tool in my newsletter over the past year. It’s a full-featured CSS and JSX generator for producing different kinds of layouts using the CSS Grid Layout syntax.

Click on any of the five layout styles, and you’ll come to an interactive online editor that lets you mess around with various CSS Grid features like rows, columns, row gap, column gap, direction, grid alignment, and lots more. There’s quite a bit to play around with here, and apparently the tool will later include the Flexbox syntax for some of the examples.

What were your favorite front-end tools of 2021?

That wraps up this year’s list of most interesting front-end tools. I hope you found something here you can bookmark or start using in a new project. It’s pretty cool that this list contained such a variety of tools — there were UI kits, animation libraries, a stock photo site, image tools, and more.

Are there front-end tools not mentioned in this list that you enjoyed discovering over the past year? Feel free to drop it in the comments. You can also subscribe to my newsletter for more front-end tools in 2022 and feel free to hit me up if you’ve built something yourself that you’d like to share in a future issue.


What Were the Hottest Front-End Tools in 2021? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/hottest-front-end-tools-in-2021/feed/ 6 361047
How to Make a Component That Supports Multiple Frameworks in a Monorepo https://css-tricks.com/make-a-component-multiple-frameworks-in-a-monorepo/ https://css-tricks.com/make-a-component-multiple-frameworks-in-a-monorepo/#comments Wed, 05 Jan 2022 15:42:59 +0000 https://css-tricks.com/?p=360239 Your mission — should you decide to accept it — is to build a Button component in four frameworks, but, only use one button.css file!

This idea is very important to me. I’ve been working on a component library called …


How to Make a Component That Supports Multiple Frameworks in a Monorepo originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Your mission — should you decide to accept it — is to build a Button component in four frameworks, but, only use one button.css file!

This idea is very important to me. I’ve been working on a component library called AgnosticUI where the purpose is building UI components that aren’t tied to any one particular JavaScript framework. AgnosticUI works in React, Vue 3, Angular, and Svelte. So that’s exactly what we’ll do today in this article: build a button component that works across all these frameworks.

The source code for this article is available on GitHub on the the-little-button-that-could-series branch.

Table of contents

Why a monorepo?

We’re going to set up a tiny Yarn workspaces-based monorepo. Why? Chris actually has a nice outline of the benefits in another post. But here’s my own biased list of benefits that I feel are relevant for our little buttons endeavor:

Coupling

We’re trying to build a single button component that uses just one button.css file across multiple frameworks. So, by nature, there’s some purposeful coupling going on between the various framework implementations and the single-source-of-truth CSS file. A monorepo setup provides a convenient structure that facilitates copying our single button.css component into various framework-based projects.

Workflow

Let’s say the button needs a tweak — like the “focus-ring” implementation, or we screwed up the use of aria in the component templates. Ideally, we’d like to correct things in one place rather than making individual fixes in separate repositories.

Testing

We want the convenience of firing up all four button implementations at the same time for testing. As this sort of project grows, it’s safe to assume there will be more proper testing. In AgnosticUI, for example, I’m currently using Storybook and often kick off all the framework Storybooks, or run snapshot testing across the entire monorepo.

I like what Leonardo Losoviz has to say about the monorepo approach. (And it just so happens to align with with everything we’ve talked about so far.)

I believe the monorepo is particularly useful when all packages are coded in the same programming language, tightly coupled, and relying on the same tooling.

Setting up

Time to dive into code — start by creating a top-level directory on the command-line to house the project and then cd into it. (Can’t think of a name? mkdir buttons && cd buttons will work fine.)

First off, let’s initialize the project:

$ yarn init
yarn init v1.22.15
question name (articles): littlebutton
question version (1.0.0): 
question description: my little button project
question entry point (index.js): 
question repository url: 
question author (Rob Levin): 
question license (MIT): 
question private: 
success Saved package.json

That gives us a package.json file with something like this:

{
  "name": "littlebutton",
  "version": "1.0.0",
  "description": "my little button project",
  "main": "index.js",
  "author": "Rob Levin",
  "license": "MIT"
}

Creating the baseline workspace

We can set the first one up with this command:

mkdir -p ./littlebutton-css

Next, we need to add the two following lines to the monorepo’s top-level package.json file so that we keep the monorepo itself private. It also declares our workspaces:

// ...
"private": true,
"workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular", "littlebutton-css"]

Now descend into the littlebutton-css directory. We’ll again want to generate a package.json with yarn init. Since we’ve named our directory littlebutton-css (the same as how we specified it in our workspaces in package.json) we can simply hit the Return key and accept all the prompts:

$ cd ./littlebutton-css && yarn init
yarn init v1.22.15
question name (littlebutton-css): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author (Rob Levin): 
question license (MIT): 
question private: 
success Saved package.json

At this point, the directory structure should look like this:

├── littlebutton-css
│   └── package.json
└── package.json

We’ve only created the CSS package workspace at this point as we’ll be generating our framework implementations with tools like vite which, in turn, generate a package.json and project directory for you. We will have to remember that the name we choose for these generated projects must match the name we’ve specified in the package.json for our earlier workspaces to work.

Baseline HTML & CSS

Let’s stay in the ./littlebutton-css workspace and create our simple button component using vanilla HTML and CSS files.

touch index.html ./css/button.css

Now our project directory should look like this:

littlebutton-css
├── css
│   └── button.css
├── index.html
└── package.json

Let’s go ahead and connect some dots with some boilerplate HTML in ./index.html:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>The Little Button That Could</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="css/button.css">
</head>
<body>
  <main>
    <button class="btn">Go</button>
  </main>
</body>
</html>

And, just so we have something visual to test, we can add a little color in ./css/button.css:

.btn {
  color: hotpink;
}
A mostly unstyled button with hot-pink text from the monorepo framework.

Now open up that index.html page in the browser. If you see an ugly generic button with hotpink text… success!

Framework-specific workspaces

So what we just accomplished is the baseline for our button component. What we want to do now is abstract it a bit so it’s extensible for other frameworks and such. For example, what if we want to use the button in a React project? We’re going to need workspaces in our monorepo for each one. We’ll start with React, then follow suit for Vue 3, Angular, and Svelte.

React

We’re going to generate our React project using vite, a very lightweight and blazingly fast builder. Be forewarned that if you attempt to do this with create-react-app, there’s a very good chance you will run into conflicts later with react-scripts and conflicting webpack or Babel configurations from other frameworks, like Angular.

To get our React workspace going, let’s go back into the terminal and cd back up to the top-level directory. From there, we’ll use vite to initialize a new project — let’s call it littlebutton-react — and, of course, we’ll select react as the framework and variant at the prompts:

$ yarn create vite
yarn create v1.22.15
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Installed "create-vite@2.6.6" with binaries:
      - create-vite
      - cva
✔ Project name: … littlebutton-react
✔ Select a framework: › react
✔ Select a variant: › react

Scaffolding project in /Users/roblevin/workspace/opensource/guest-posts/articles/littlebutton-react...

Done. Now run:

  cd littlebutton-react
  yarn
  yarn dev

✨  Done in 17.90s.

We initialize the React app with these commands next:

cd littlebutton-react
yarn
yarn dev

With React installed and verified, let’s replace the contents of src/App.jsx to house our button with the following code:

import "./App.css";

const Button = () => {
  return <button>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button />
    </div>
  );
}

export default App;

Now we’re going to write a small Node script that copies our littlebutton-css/css/button.css right into our React application for us. This step is probably the most interesting one to me because it’s both magical and ugly at the same time. It’s magical because it means our React button component is truly deriving its styles from the same CSS written in the baseline project. It’s ugly because, well, we are reaching up out of one workspace and grabbing a file from another. ¯\_(ツ)_/¯

Add the following little Node script to littlebutton-react/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/button.css", css, "utf8");

Let’s place a node command to run that in a package.json script that happens before the dev script in littlebutton-react/package.json. We’ll add a syncStyles and update the dev to call syncStyles before vite:

"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",

Now, anytime we fire up our React application with yarn dev, we’ll first be copying the CSS file over. In essence, we’re “forcing” ourselves to not diverge from the CSS package’s button.css in our React button.

But we want to also leverage CSS Modules to prevent name collisions and global CSS leakage, so we have one more step to do to get that wired up (from the same littlebutton-react directory):

touch src/button.module.css

Next, add the following to the new src/button.module.css file:

.btn {
  composes: btn from './button.css';
}

I find composes (also known as composition) to be one of the coolest features of CSS Modules. In a nutshell, we’re copying our HTML/CSS version of button.css over wholesale then composing from our one .btn style rule.

With that, we can go back to our src/App.jsx and import the CSS Modules styles into our React component with this:

import "./App.css";
import styles from "./button.module.css";

const Button = () => {
  return <button className={styles.btn}>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button />
    </div>
  );
}

export default App;

Whew! Let’s pause and try to run our React app again:

yarn dev

If all went well, you should see that same generic button, but with hotpink text. Before we move on to the next framework, let’s move back up to our top-level monorepo directory and update its package.json:

{
  "name": "littlebutton",
  "version": "1.0.0",
  "description": "toy project",
  "main": "index.js",
  "author": "Rob Levin",
  "license": "MIT",
  "private": true,
  "workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular"],
  "scripts": {
    "start:react": "yarn workspace littlebutton-react dev"
  }
}

Run the yarn command from the top-level directory to get the monorepo-hoisted dependencies installed.

The only change we’ve made to this package.json is a new scripts section with a single script to start the React app. By adding start:react we can now run yarn start:react from our top-level directory and it will fire up the project we just built in ./littlebutton-react without the need for cd‘ing — super convenient!

We’ll tackle Vue and Svelte next. It turns out that we can take a pretty similar approach for these as they both use single file components (SFC). Basically, we get to mix HTML, CSS, and JavaScript all into one single file. Whether you like the SFC approach or not, it’s certainly adequate enough for building out presentational or primitive UI components.

Vue

Following the steps from vite’s scaffolding docs we’ll run the following command from the monorepo’s top-level directory to initialize a Vue app:

yarn create vite littlebutton-vue --template vue

This generates scaffolding with some provided instructions to run the starter Vue app:

cd littlebutton-vue
yarn
yarn dev

This should fire up a starter page in the browser with some heading like “Hello Vue 3 + Vite.” From here, we can update src/App.vue to:

<template>
  <div id="app">
    <Button class="btn">Go</Button>
  </div>
</template>

<script>
import Button from './components/Button.vue'

export default {
  name: 'App',
  components: {
    Button
  }
}
</script>

And we’ll replace any src/components/* with src/components/Button.vue:

<template>
  <button :class="classes"><slot /></button>
</template>

<script>
export default {
  name: 'Button',
  computed: {
    classes() {
      return {
        [this.$style.btn]: true,
      }
    }
  }
}
</script>

<style module>
.btn {
  color: slateblue;
}
</style>

Let’s break this down a bit:

  • :class="classes" is using Vue’s binding to call the computed classes method.
  • The classes method, in turn, is utilizing CSS Modules in Vue with the this.$style.btn syntax which will use styles contained in a <style module> tag.

For now, we’re hardcoding color: slateblue simply to test that things are working properly within the component. Try firing up the app again with yarn dev. If you see the button with our declared test color, then it’s working!

Now we’re going to write a Node script that copies our littlebutton-css/css/button.css into our Button.vue file similar to the one we did for the React implementation. As mentioned, this component is a SFC so we’re going to have to do this a little differently using a simple regular expression.

Add the following little Node.js script to littlebutton-vue/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const vue = fs.readFileSync("./src/components/Button.vue", "utf8");
// Take everything between the starting and closing style tag and replace
const styleRegex = /<style module>([\s\S]*?)<\/style>/;
let withSynchronizedStyles = vue.replace(styleRegex, `<style module>\n${css}\n</style>`);
fs.writeFileSync("./src/components/Button.vue", withSynchronizedStyles, "utf8");

There’s a bit more complexity in this script, but using replace to copy text between opening and closing style tags via regex isn’t too bad.

Now let’s add the following two scripts to the scripts clause in the littlebutton-vue/package.json file:

"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",

Now run yarn syncStyles and look at ./src/components/Button.vue again. You should see that our style module gets replaced with this:

<style module>
.btn {
  color: hotpink;
}
</style>

Run the Vue app again with yarn dev and verify you get the expected results — yes, a button with hotpink text. If so, we’re good to move on to the next framework workspace!

Svelte

Per the Svelte docs, we should kick off our littlebutton-svelte workspace with the following, starting from the monorepo’s top-level directory:

npx degit sveltejs/template littlebutton-svelte
cd littlebutton-svelte
yarn && yarn dev

Confirm you can hit the “Hello World” start page at http://localhost:5000. Then, update littlebutton-svelte/src/App.svelte:

<script>
  import Button from './Button.svelte';
</script>
<main>
  <Button>Go</Button>
</main>

Also, in littlebutton-svelte/src/main.js, we want to remove the name prop so it looks like this:

import App from './App.svelte';

const app = new App({
  target: document.body
});

export default app;

And finally, add littlebutton-svelte/src/Button.svelte with the following:

<button class="btn">
  <slot></slot>
</button>

<script>
</script>

<style>
  .btn {
    color: saddlebrown;
  }
</style>

One last thing: Svelte appears to name our app: "name": "svelte-app" in the package.json. Change that to "name": "littlebutton-svelte" so it’s consistent with the workspaces name in our top-level package.json file.

Once again, we can copy our baseline littlebutton-css/css/button.css into our Button.svelte. As mentioned, this component is a SFC, so we’re going to have to do this using a regular expression. Add the following Node script to littlebutton-svelte/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const svelte = fs.readFileSync("./src/Button.svelte", "utf8");
const styleRegex = /<style>([\s\S]*?)<\/style>/;
let withSynchronizedStyles = svelte.replace(styleRegex, `<style>\n${css}\n</style>`);
fs.writeFileSync("./src/Button.svelte", withSynchronizedStyles, "utf8");

This is super similar to the copy script we used with Vue, isn’t it? We’ll add similar scripts to our package.json script:

"dev": "yarn syncStyles && rollup -c -w",
"syncStyles": "node copystyles.js",

Now run yarn syncStyles && yarn dev. If all is good, we once again should see a button with hotpink text.

If this is starting to feel repetitive, all I have to say is welcome to my world. What I’m showing you here is essentially the same process I’ve been using to build my AgnosticUI project!

Angular

You probably know the drill by now. From the monorepo’s top-level directory, install Angular and create an Angular app. If we were creating a full-blown UI library we’d likely use ng generate library or even nx. But to keep things as straightforward as possible we’ll set up a boilerplate Angular app as follows:

npm install -g @angular/cli ### unless you already have installed
ng new littlebutton-angular ### choose no for routing and CSS
? Would you like to add Angular routing? (y/N) N
❯ CSS 
  SCSS   [ https://sass-lang.com/documentation/syntax#scss ] 
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] 
  Less   [ http://lesscss.org ]

cd littlebutton-angular && ng serve --open

With the Angular setup confirmed, let’s update some files. cd littlebutton-angular, delete the src/app/app.component.spec.ts file, and add a button component in src/components/button.component.ts, like this:

import { Component } from '@angular/core';

@Component({
  selector: 'little-button',
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.css'],
})
export class ButtonComponent {}

Add the following to src/components/button.component.html:

<button class="btn">Go</button>

And put this in the src/components/button.component.css file for testing:

.btn {
  color: fuchsia;
}

In src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { ButtonComponent } from '../components/button.component';

@NgModule({
  declarations: [AppComponent, ButtonComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Next, replace src/app/app.component.ts with:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {}

Then, replace src/app/app.component.html with:

<main>
  <little-button>Go</little-button>
</main>

With that, let’s run yarn start and verify our button with fuchsia text renders as expected.

Again, we want to copy over the CSS from our baseline workspace. We can do that by adding this to littlebutton-angular/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/components/button.component.css", css, "utf8");

Angular is nice in that it uses ViewEncapsulation that defaults to to emulate which mimics, according to the docs,

[…] the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component’s view.

This basically means we can literally copy over button.css and use it as-is.

Finally, update the package.json file by adding these two lines in the scripts section:

"start": "yarn syncStyles && ng serve",
"syncStyles": "node copystyles.js",

With that, we can now run yarn start once more and verify our button text color (which was fuchsia) is now hotpink.

What have we just done?

Let’s take a break from coding and think about the bigger picture and what we’ve just done. Basically, we’ve set up a system where any changes to our CSS package’s button.css will get copied over into all the framework implementations as a result of our copystyles.js Node scripts. Further, we’ve incorporated idiomatic conventions for each of the frameworks:

  • SFC for Vue and Svelte
  • CSS Modules for React (and Vue within the SFC <style module> setup)
  • ViewEncapsulation for Angular

Of course I state the obvious that these aren’t the only ways to do CSS in each of the above frameworks (e.g. CSS-in-JS is a popular choice), but they are certainly accepted practices and are working quite well for our greater goal — to have a single CSS source of truth to drive all framework implementations.

If, for example, our button was in use and our design team decided we wanted to change from 4px to 3px border-radius, we could update the one file, and any separate implementations would stay synced.

This is compelling if you have a polyglot team of developers that enjoy working in multiple frameworks, or, say an offshore team (that’s 3× productive in Angular) that’s being tasked to build a back-office application, but your flagship product is built in React. Or, you’re building an interim admin console and you’d love to experiment with using Vue or Svelte. You get the picture.

Finishing touches

OK, so we have the monorepo architecture in a really good spot. But there’s a few things we can do to make it even more useful as far as the developer experience goes.

Better start scripts

Let’s move back up to our top-level monorepo directory and update its package.json scripts section with the following so we can kick any framework implementation without cd‘ing:

// ...
"scripts": {
  "start:react": "yarn workspace littlebutton-react dev",
  "start:vue": "yarn workspace littlebutton-vue dev ",
  "start:svelte": "yarn workspace littlebutton-svelte dev",
  "start:angular": "yarn workspace littlebutton-angular start"
},

Better baseline styles

We can also provide a better set of baseline styles for the button so it starts from a nice, neutral place. Here’s what I did in the littlebutton-css/css/button.css file.

View Full Snippet
.btn {
  --button-dark: #333;
  --button-line-height: 1.25rem;
  --button-font-size: 1rem;
  --button-light: #e9e9e9;
  --button-transition-duration: 200ms;
  --button-font-stack:
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    Ubuntu,
    "Helvetica Neue",
    sans-serif;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  user-select: none;
  appearance: none;
  cursor: pointer;
  box-sizing: border-box;
  transition-property: all;
  transition-duration: var(--button-transition-duration);
  color: var(--button-dark);
  background-color: var(--button-light);
  border-color: var(--button-light);
  border-style: solid;
  border-width: 1px;
  font-family: var(--button-font-stack);
  font-weight: 400;
  font-size: var(--button-font-size);
  line-height: var(--button-line-height);
  padding-block-start: 0.5rem;
  padding-block-end: 0.5rem;
  padding-inline-start: 0.75rem;
  padding-inline-end: 0.75rem;
  text-decoration: none;
  text-align: center;
}

/* Respect users reduced motion preferences */
@media (prefers-reduced-motion) {
  .btn {
    transition-duration: 0.001ms !important;
  }
}

Let’s test this out! Fire up each of the four framework implementations with the new and improved start scripts and confirm the styling changes are in effect.

Neutral (gray) styled button from the monorepo framework

One CSS file update proliferated to four frameworks — pretty cool, eh!?

Set a primary mode

We’re going to add a mode prop to each of our button’s and implement primary mode next. A primary button could be any color but we’ll go with a shade of green for the background and white text. Again, in the baseline stylesheet:

.btn {
  --button-primary: #14775d;
  --button-primary-color: #fff;
  /* ... */
}

Then, just before the @media (prefers-reduced-motion) query, add the following btn-primary to the same baseline stylesheet:

.btn-primary {
  background-color: var(--button-primary);
  border-color: var(--button-primary);
  color: var(--button-primary-color);
}

There we go! Some developer conveniences and better baseline styles!

Updating each component to take a mode property

Now that we’ve added our new primary mode represented by the .btn-primary class, we want to sync the styles for all four framework implementations. So, let’s add some more package.json scripts to our top level scripts:

"sync:react": "yarn workspace littlebutton-react syncStyles",
"sync:vue": "yarn workspace littlebutton-vue syncStyles",
"sync:svelte": "yarn workspace littlebutton-svelte syncStyles",
"sync:angular": "yarn workspace littlebutton-angular syncStyles"

Be sure to respect JSON’s comma rules! Depending on where you place these lines within your scripts: {...}, you’ll want to make sure there are no missing or trailing commas.

Go ahead and run the following to fully synchronize the styles:

yarn sync:angular && yarn sync:react && yarn sync:vue && yarn sync:svelte

Running this doesn’t change anything because we haven’t applied the primary class yet, but you should at least see the CSS has been copied over if you go look at the framework’s button component CSS.

React

If you haven’t already, double-check that the updated CSS got copied over into littlebutton-react/src/button.css. If not, you can run yarn syncStyles. Note that if you forget to run yarn syncStyles our dev script will do this for us when we next start the application anyway:

"dev": "yarn syncStyles && vite",

For our React implementation, we additionally need to add a composed CSS Modules class in littlebutton-react/src/button.module.css that is composed from the new .btn-primary:

.btnPrimary {
  composes: btn-primary from './button.css';
}

We’ll also update littlebutton-react/src/App.jsx:

import "./App.css";
import styles from "./button.module.css";

const Button = ({ mode }) => {
  const primaryClass = mode ? styles[`btn${mode.charAt(0).toUpperCase()}${mode.slice(1)}`] : '';
  const classes = primaryClass ? `${styles.btn} ${primaryClass}` : styles.btn;
  return <button className={classes}>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button mode="primary" />
    </div>
  );
}

export default App;

Fire up the React app with yarn start:react from the top-level directory. If all goes well, you should now see your green primary button.

A dark green button with white text positioning in the center of the screen.

As a note, I’m keeping the Button component in App.jsx for brevity. Feel free to tease out the Button component into its own file if that bothers you.

Vue

Again, double-check that the button styles were copied over and, if not, run yarn syncStyles.

Next, make the following changes to the <script> section of littlebutton-vue/src/components/Button.vue:

<script>
export default {
  name: 'Button',
  props: {
    mode: {
      type: String,
      required: false,
      default: '',
      validator: (value) => {
        const isValid = ['primary'].includes(value);
        if (!isValid) {
          console.warn(`Allowed types for Button are primary`);
        }
        return isValid;
      },
    }
  },
  computed: {
    classes() {
      return {
        [this.$style.btn]: true,
        [this.$style['btn-primary']]: this.mode === 'primary',
      }
    }
  }
}
</script>

Now we can update the markup in littlebutton-vue/src/App.vue to use the new mode prop:

<Button mode="primary">Go</Button>

Now you can yarn start:vue from the top-level directory and check for the same green button.

Svelte

Let’s cd into littlebutton-svelte and verify that the styles in littlebutton-svelte/src/Button.svelte have the new .btn-primary class copied over, and yarn syncStyles if you need to. Again, the dev script will do that for us anyway on the next startup if you happen to forget.

Next, update the Svelte template to pass the mode of primary. In src/App.svelte:

<script>
  import Button from './Button.svelte';
</script>
<main>
  <Button mode="primary">Go</Button>
</main>

We also need to update the top of our src/Button.svelte component itself to accept the mode prop and apply the CSS Modules class:

<button class="{classes}">
  <slot></slot>
</button>
<script>
  export let mode = "";
  const classes = [
    "btn",
    mode ? `btn-${mode}` : "",
  ].filter(cls => cls.length).join(" ");
</script>

Note that the <styles> section of our Svelte component shouldn’t be touched in this step.

And now, you can yarn dev from littlebutton-svelte (or yarn start:svelte from a higher directory) to confirm the green button made it!

Angular

Same thing, different framework: check that the styles are copied over and run yarn syncStyles if needed.

Let’s add the mode prop to the littlebutton-angular/src/app/app.component.html file:

<main>
  <little-button mode="primary">Go</little-button>
</main>

Now we need to set up a binding to a classes getter to compute the correct classes based on if the mode was passed in to the component or not. Add this to littlebutton-angular/src/components/button.component.html (and note the binding is happening with the square brackets):

<button [class]="classes">Go</button>

Next, we actually need to create the classes binding in our component at littlebutton-angular/src/components/button.component.ts:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'little-button',
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.css'],
})
export class ButtonComponent {
  @Input() mode: 'primary' | undefined = undefined;

  public get classes(): string {
    const modeClass = this.mode ? `btn-${this.mode}` : '';
    return [
      'btn',
      modeClass,
    ].filter(cl => cl.length).join(' ');
  }
}

We use the Input directive to take in the mode prop, then we create a classes accessor which adds the mode class if it’s been passed in.

Fire it up and look for the green button!

Code complete

If you’ve made it this far, congratulations — you’ve reached code complete! If something went awry, I’d encourage you to cross-reference the source code over at GitHub on the the-little-button-that-could-series branch. As bundlers and packages have a tendency to change abruptly, you might want to pin your package versions to the ones in this branch if you happen to experience any dependency issues.

Take a moment to go back and compare the four framework-based button component implementations we just built. They’re still small enough to quickly notice some interesting differences in how props get passed in, how we bind to props, and how CSS name collisions are prevented among other subtle differences. As I continue to add components to AgnosticUI (which supports these exact same four frameworks), I’m continually pondering which offers the best developer experience. What do you think?

Homework

If you’re the type that likes to figure things out on your own or enjoys digging in deeper, here are ideas.

Button states

The current button styles do not account for various states, like :hover. I believe that’s a good first exercise.

/* You should really implement the following states
   but I will leave it as an exercise for you to 
   decide how to and what values to use.
*/
.btn:focus {
  /* If you elect to remove the outline, replace it
     with another proper affordance and research how
     to use transparent outlines to support windows
     high contrast
  */
}
.btn:hover { }
.btn:visited { }
.btn:active { }
.btn:disabled { }

Variants

Most button libraries support many button variations for things like sizes, shapes, and colors. Try creating more than the primary mode we already have. Maybe a secondary variation? A warning or success? Maybe filled and outline? Again, you can look at AgnosticUI’s buttons page for ideas.

CSS custom properties

If you haven’t started using CSS custom properties yet, I’d strongly recommend it. You can start by having a look at AgnosticUI’s common styles. I heavily lean on custom properties in there. Here are some great articles that cover what custom properties are and how you might leverage them:

Types

No… not typings, but the <button> element’s type attribute. We didn’t cover that in our component but there’s an opportunity to extend the component to other use cases with valid types, like button, submit, and reset. This is pretty easy to do and will greatly improve the button’s API.

More ideas

Gosh, you could do so much — add linting, convert it to Typescript, audit the accessibility, etc.

The current Svelte implementation is suffering from some pretty loose assumptions as we have no defense if the valid primary mode isn’t passed — that would produce a garbage CSS class:

mode ? `btn-${mode}` : "",

You could say, “Well, .btn-garbage as a class isn’t exactly harmful.” But it’s probably a good idea to style defensively when and where possible.

Potential pitfalls

There are some things you should be aware of before taking this approach further:

  • Positional CSS based on the structure of the markup will not work well for the CSS Modules based techniques used here.
  • Angular makes positional techniques even harder as it generates :host element representing each component view. This means you have these extra elements in between your template or markup structure. You’ll need to work around that.
  • Copying styles across workspace packages is a bit of an anti-pattern to some folks. I justify it because I believe the benefits outweigh the costs; also, when I think about how monorepos use symlinks and (not-so-failproof) hoisting, I don’t feel so bad about this approach.
  • You’ll have to subscribe to the decoupled techniques used here, so no CSS-in-JS.

I believe that all approaches to software development have their pros and cons and you ultimately have to decide if sharing a single CSS file across frameworks works for you or your specific project. There are certainly other ways you could do this (e.g. using littlebuttons-css as an npm package dependency) if needed.

Conclusion

Hopefully I’ve whet your appetite and you’re now really intrigued to create UI component libraries and/or design systems that are not tied to a particular framework. Maybe you have a better idea on how to achieve this — I’d love to hear your thoughts in the comments!

I’m sure you’ve seen the venerable TodoMVC project and how many framework implementations have been created for it. Similarly, wouldn’t it be nice to have a UI component library of primitives available for many frameworks? Open UI is making great strides to properly standardize native UI component defaults, but I believe we’ll always need to insert ourselves to some extent. Certainly, taking a good year to build a custom design system is quickly falling out of favor and companies are seriously questioning their ROI. Some sort of scaffolding is required to make the endeavor practical.

The vision of AgnosticUI is to have a relatively agnostic way to build design systems quickly that are not tied down to a particular frontend framework. If you’re compelled to get involved, the project is still very early and approachable and I’d love some help! Plus, you’re already pretty familiar with the how the project works now that you’ve gone through this tutorial!


How to Make a Component That Supports Multiple Frameworks in a Monorepo originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/make-a-component-multiple-frameworks-in-a-monorepo/feed/ 3 360239
Tonic (Component Framework) https://css-tricks.com/tonic-component-framework/ https://css-tricks.com/tonic-component-framework/#comments Mon, 27 Sep 2021 18:52:46 +0000 https://css-tricks.com/?p=352621 I enjoy little frameworks like Tonic. It’s essentially syntactic sugar over <web-components /> to make them feel easier to use. Define a Class, template literal an HTML template, probably some other fancy helpers, and you’ve got a component …


Tonic (Component Framework) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I enjoy little frameworks like Tonic. It’s essentially syntactic sugar over <web-components /> to make them feel easier to use. Define a Class, template literal an HTML template, probably some other fancy helpers, and you’ve got a component that doesn’t feel terribly different to something like a React component, except you need no build process or other exotic tooling.

Here’s a Hello World + Counter example:

They have a whole bunch of examples (in a separate repo). You can snag and use them, and they are pretty nice! So that makes Tonic a bit like a design system as well as a web component framework.

To be fair, it’s not that different from Lit, which Google is behind and pushing pretty actively.

Here’s a Hello, World + Counter with Lit:

And Dave was just showing me petite-vue the other day, so I figured I might as well do that one, too:

I’d say that petite-vue example wins for just how super easy that is to pull of in just declarative HTML. But of course, there are a bunch of other considerations from specific features, syntax, philosophy, and size. Just looking at size, if I pop open the Network tab in DevTools and see the over-the-wire JavaScript for each demo…

  • Tonic = 5.1 KB
  • Lit = 12.6 KB
  • petite-vue = 8.1 KB

They are all basically the same: tiny.

I’ve never actually built anything real in any of them, so I’m not the best to judge one from the other. But they all seem pretty neat to me, particularly because they require no build step.

Like Video?

Dave and I went through all this:


Tonic (Component Framework) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/tonic-component-framework/feed/ 1 352621
Developers and Designers Work on a Single Source of Truth With UXPin https://css-tricks.com/developers-designers-uxpin/ https://css-tricks.com/developers-designers-uxpin/#respond Mon, 13 Sep 2021 19:21:00 +0000 https://css-tricks.com/?p=350466 (This is a sponsored post.)

There is a conversation that has been percolating for as long as I’ve been in the web design and development industry. It’s centered around the conflict between design tools and development tools. The final …


Developers and Designers Work on a Single Source of Truth With UXPin originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

There is a conversation that has been percolating for as long as I’ve been in the web design and development industry. It’s centered around the conflict between design tools and development tools. The final product of web design is often a mockup. The old joke was that web developers make websites and web designers make paintings of websites. That disconnect is a source of immense friction. Which is the source of truth?

What if there really could be a single source of truth. What if the design tool works on the same exact code as the production website? The latest chapter in this epic conversation is UXPin.

Let’s set up the facts so you can see this all play out.

UXPin is an in-browser & code-based design tool.

UXPin is a powerful design tool with all the features you’d expect, particularly focused on digital screen-based design and advanced prototyping.

The fact that it is code-based is extra great here. Designing websites with all the visual components actually rooted in code brings the design much closer to the real end-product. What you design won’t only look like a website or app but also work like it. For example, an input field is not a static box with an outline, but it’ll give you the real experience of filling it with text.

Code-based design already provides all the specs for each element – like with this card component; exact colors (in the right formats), as well as the exact pixel dimensions, etc. In some cases – even the exact right code of the UI component for your dev can be pulled.

This is laid out nicely by Ania Kubów in a video about UXPin.


Over a decade ago, Jason Santa Maria thought a lot about what a next-gen design tool would look like. Could we just use the browser directly?

I don’t think the browser is enough. A web designer jumping into the browser before tackling the creative and messaging problems is akin to an architect hammering pieces of wood together and then measuring afterwards. The imaginative process is cut short by the tools at hand; and it’s that imagination—or spark—at the beginning of a design that lays the path for everything that follows.

Jason Santa Maria, “A Real Web Design Application”

Perhaps not the browser directly, but a code-based tool that makes UI work like your website or app could be the best of both worlds:

Webpages are living, dynamic spaces where the smallest interaction from a visitor can change the scope of an entire site. […] Because we’re not dealing with a static medium, we need to be able to design for interactions and the shifting landscapes of a webpage […] an application needs to see elements rather than blocks of color or text. Photoshop, Illustrator, and Fireworks have some low-level functionality in this regard, but the need for more dynamic and non-destructive handling is clear.

You can work on your own React components in UXPin.

This is where the single source of truth magic can happen. It’s one thing if a design tool can output a React (or any other framework) component. That’s a neat trick. But it’s likely to be a one-way trip. Components in real-world projects are full of other things that aren’t entirely the domain of design. Perhaps a component uses a hook to return the current user’s permissions and disable a button if they don’t have access. The disabled button has an element of design to it, but most of that code does not.

It’s impractical to have a design tool that can’t respect other code in that component and essentially just leave it alone. Essentially, the design tool is not that useful if it exports components as code but doesn’t allow designers to import those UI components in the first place.

This is where UXPin Merge comes in.

Now, fair is fair, this is going to take a little work to set up. Might just be a couple of hours, or it might take a few weeks for a complete design system. UXPin, for now, only works with React and uses a webpack configuration to integrate it.

Once you’ve gotten in going, the components you use in UXPin are very literally the components you use to build your production website.

It’s pretty impressive really, to see a design tool digest pre-built components and allow them to be used on an entirely new canvas for prototyping.

UXPin helps you with implementing this in your project, including:

As it should, it’s likely to influence how you build components.

Components tend to have props, and props control things like design and content inside. UXPin gives you a UI for the props, meaning you have total control over the component.

<LineChart 
  barColor="green"
  height="200"
  width="500"
  showXAxis="false"
  showYAxis="true"
  data={[ ... ]}
/>

Knowing that, you might give yourself a prop interface for your components that provides you with lots of design control. For example, integrating theme switching.

This is all even faster with Storybook.

Another awfully popular tool in JavaScript-components-land to test and build your components is Storybook. It’s not a design tool like UXPin—it’s more like a zoo for your components. You might already have it set up, or you might find value in using Storybook as well.

The great news? UXPin Merge works together awesomely with Storybook. It makes integration super quick and easy. Plus then it supports any framework, like Angular, Svelte, Vue, etc—in addition to React.

Look how fast:

UXPin CEO Marcin Treder had a strong vision:

What if designers could use the very same components used by engineers and they’re all stored in a shared design system (with accurate documentation and tests)? Many of the frustrating and expensive misunderstandings between designers and engineers would stop happening.

And a plan:

  1. Connect to Git repo or Storybook library.
  2. Import components from there to UXPin design tool.
  3. All the changes in the repo will be synced automatically in UXPin Watch for any changes to the repo and sync those changes in the visual editor.
  4. Let designers design and deliver accurate specs and fully functional design to developers.

And that’s what they’ve pulled off here.


Developers and Designers Work on a Single Source of Truth With UXPin originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/developers-designers-uxpin/feed/ 0 350466
Creating UI Components in SVG https://css-tricks.com/creating-ui-components-in-svg/ https://css-tricks.com/creating-ui-components-in-svg/#comments Mon, 23 Nov 2020 23:35:41 +0000 https://css-tricks.com/?p=325860 I’m thoroughly convinced that SVG unlocks a whole entire world of building interfaces on the web. It might seem daunting to learn SVG at first, but you have a spec that was designed to create shapes and yet, still has …


Creating UI Components in SVG originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I’m thoroughly convinced that SVG unlocks a whole entire world of building interfaces on the web. It might seem daunting to learn SVG at first, but you have a spec that was designed to create shapes and yet, still has elements, like text, links, and aria labels available to you. You can accomplish some of the same effects in CSS, but it’s a little more particular to get positioning just right, especially across viewports and for responsive development.

What’s special about SVG is that all the positioning is based on a coordinate system, a little like the game Battleship. That means deciding where everything goes and how it’s drawn, as well as how it’s relative to each other, can be really straightforward to reason about. CSS positioning is for layout, which is great because you have things that correspond to one another in terms of the flow of the document. This otherwise positive trait is harder to work with if you’re making a component that’s very particular, with overlapping and precisely placed elements.

Truly, once you learn SVG, you can draw anything, and have it scale on any device. Even this very site uses SVG for custom UI elements, such as my avatar, above (meta!).

That little half circle below the author image is just SVG markup.

We won’t cover everything about SVGs in this post (you can learn some of those fundamentals here, here, here and here), but in order to illustrate the possibilities that SVG opens up for UI component development, let’s talk through one particular use case and break down how we would think about building something custom.

The timeline task list component

Recently, I was working on a project with my team at Netlify. We wanted to show the viewer which video in a series of videos in a course they were currently watching. In other words, we wanted to make some sort of thing that’s like a todo list, but shows overall progress as items are completed. (We made a free space-themed learning platform and it’s hella cool. Yes, I said hella.)

Here’s how that looks:

So how would we go about this? I’ll show an example in both Vue and React so that you can see how it might work in both frameworks.

The Vue version

We decided to make the platform in Next.js for dogfooding purposes (i.e. trying out our own Next on Netlify build plugin), but I’m more fluent in Vue so I wrote the initial prototype in Vue and ported it over to React.

Here is the full CodePen demo:

Let’s walk through this code a bit. First off, this is a single file component (SFC), so the template HTML, reactive script, and scoped styles are all encapsulated in this one file.

We’ll store some dummy tasks in data, including whether each task is completed or not. We’ll also make a method we can call on a click directive so that we can toggle whether the state is done or not.

<script>
export default {
  data() {
    return {
      tasks: [
        {
          name: 'thing',
          done: false
        },
        // ...
      ]
    };
  },
  methods: {
    selectThis(index) {
      this.tasks[index].done = !this.tasks[index].done
    }
  }
};
</script>

Now, what we want to do is create an SVG that has a flexible viewBox depending on the amount of elements. We also want to tell screen readers that this a presentational element and that we will provide a title with a unique id of timeline. (Get more information on creating accessible SVGs.)

<template>
  <div id="app">
    <div>
      <svg :viewBox="`0 0 30 ${tasks.length * 50}`"
           xmlns="http://www.w3.org/2000/svg" 
           width="30" 
           stroke="currentColor" 
           fill="white"
           aria-labelledby="timeline"
           role="presentation">
           <title id="timeline">timeline element</title>
        <!-- ... -->
      </svg>
    </div>
  </div>
</template>

The stroke is set to currentColor to allow for some flexibility — if we want to reuse the component in multiple places, it will inherit whatever color is used on the encapsulating div.

Next, inside the SVG, we want to create a vertical line that’s the length of the task list. Lines are fairly straightforward. We have x1 and x2 values (where the line is plotted on the x-axis), and similarly, y1 and y2.

<line x1="10" x2="10" :y1="num2" :y2="tasks.length * num1 - num2" />

The x-axis stays consistently at 10 because we’re drawing a line downward rather than left-to-right. We’ll store two numbers in data: the amount we want our spacing to be, which will be num1, and the amount we want our margin to be, which will be num2.

data() {
  return {
    num1: 32,
    num2: 15,
    // ...
  }
}

The y-axis starts with num2, which is subtracted from the end, as well as the margin. The tasks.length is multiplied by the spacing, which is num1.

Now, we’ll need the circles that lie on the line. Each circle is an indicator for whether a task has been completed or not. We’ll need one circle for each task, so we’ll use v-for with a unique key, which is the index (and is safe to use here as they will never reorder). We’ll connect the click directive with our method and pass in the index as a param as well.

CIrcles in SVG are made up of three attributes. The middle of the circle is plotted at cx and cy, and then we draw a radius with r. Like the line, cx starts at 10. The radius is 4 because that’s what’s readable at this scale. cy will be spaced like the line: index times the spacing (num1), plus the margin (num2).

Finally, we’ll put use a ternary to set the fill. If the task is done, it will be filled with currentColor. If not, it will be filled with white (or whatever the background is). This could be filled with a prop that gets passed in the background, for instance, where you have light and dark circles.

<circle 
  @click="selectThis(i)" 
  v-for="(task, i) in tasks"
  :key="task.name"
  cx="10"
  r="4"
  :cy="i * num1 + num2"
  :fill="task.done ? 'currentColor' : 'white'"
  class="select"/>

Finally, we are using CSS grid to align a div with the names of tasks. This is laid out much in the same way, where we’re looping through the tasks, and are also tied to that same click event to toggle the done state.

<template>
  <div>
    <div 
      @click="selectThis(i)"
      v-for="(task, i) in tasks"
      :key="task.name"
      class="select">
      {{ task.name }}
    </div>
  </div>
</template>

The React version

Here is where we ended up with the React version. We’re working towards open sourcing this so that you can see the full code and its history. Here are a few modifications:

  • We’re using CSS modules rather than the SCFs in Vue
  • We’re importing the Next.js link, so that rather than toggling a “done” state, we’re taking a user to a dynamic page in Next.js
  • The tasks we’re using are actually stages of the course —or “Mission” as we call them — which are passed in here rather than held by the component.

Most of the other functionality is the same :)

import styles from './MissionTracker.module.css';
import React, { useState } from 'react';
import Link from 'next/link';

function MissionTracker({ currentMission, currentStage, stages }) {
 const [tasks, setTasks] = useState([...stages]);
 const num1 = 32;
 const num2 = 15;

 const updateDoneTasks = (index) => () => {
   let tasksCopy = [...tasks];
   tasksCopy[index].done = !tasksCopy[index].done;
   setTasks(tasksCopy);
 };

 const taskTextStyles = (task) => {
   const baseStyles = `${styles['tracker-select']} ${styles['task-label']}`;

   if (currentStage === task.slug.current) {
     return baseStyles + ` ${styles['is-current-task']}`;
   } else {
     return baseStyles;
   }
 };

 return (
   <div className={styles.container}>
     <section>
       {tasks.map((task, index) => (
         <div
           key={`mt-${task.slug}-${index}`}
           className={taskTextStyles(task)}
         >
           <Link href={`/learn/${currentMission}/${task.slug.current}`}>
             {task.title}
           </Link>
         </div>
       ))}
     </section>

     <section>
       <svg
         viewBox={`0 0 30 ${tasks.length * 50}`}
         className={styles['tracker-svg']}
         xmlns="http://www.w3.org/2000/svg"
         width="30"
         stroke="currentColor"
         fill="white"
         aria-labelledby="timeline"
         role="presentation"
       >
         <title id="timeline">timeline element</title>

         <line x1="10" x2="10" y1={num2} y2={tasks.length * num1 - num2} />
         {tasks.map((task, index) => (
           <circle
             key={`mt-circle-${task.name}-${index}`}
             onClick={updateDoneTasks(index)}
             cx="10"
             r="4"
             cy={index * +num1 + +num2}
             fill={
               task.slug.current === currentStage ? 'currentColor' : 'black'
             }
             className={styles['tracker-select']}
           />
         ))}
       </svg>
     </section>
   </div>
 );
}

export default MissionTracker;

Final version

You can see the final working version here:

This component is flexible enough to accommodate lists small and large, multiple browsers, and responsive sizing. It also allows the user to have better understanding of where they are in their progress in the course.

But this is just one component. You can make any number of UI elements: knobs, controls, progress indicators, loaders… the sky’s the limit. You can style them with CSS, or inline styles, you can have them update based on props, on context, on reactive data, the sky’s the limit! I hope this opens some doors on how you yourself can develop more engaging UI elements for the web.


Creating UI Components in SVG originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/creating-ui-components-in-svg/feed/ 13 325860
How to Make a List Component with Emotion https://css-tricks.com/how-to-make-a-list-component-with-emotion/ https://css-tricks.com/how-to-make-a-list-component-with-emotion/#respond Wed, 08 Jul 2020 14:46:38 +0000 https://css-tricks.com/?p=316298 I’ve been doing a bit of refactoring this week at Sentry and I noticed that we didn’t have a generic List component that we could use across projects and features. So, I started one, but here’s the rub: we style …


How to Make a List Component with Emotion originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I’ve been doing a bit of refactoring this week at Sentry and I noticed that we didn’t have a generic List component that we could use across projects and features. So, I started one, but here’s the rub: we style things at Sentry using Emotion, which I have only passing experience with and is described in the docs as…

[…] a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

If you’ve never heard of Emotion, the general idea is this: when we’re working on big codebases with lots of components, we want to ensure that we can control the cascade of our CSS. So, let’s say you have an .active class in one file and you want to make sure that doesn’t impact the styles of a completely separate component in another file that also has a class of.active.

Emotion tackles this problem by adding custom strings to your classnames so they don’t conflict with other components. Here’s an example of the HTML it might output:

<div class="css-1tfy8g7-List e13k4qzl9"></div>

Pretty neat, huh? There’s lots of other tools and workflows out there though that do something very similar, such as CSS Modules.

To get started making the component, we first need to install Emotion into our project. I’m not going to walkthrough that stuff because it’s going to be different depending on your environment and setup. But once that’s complete we can go ahead and create a new component like this:

import React from 'react';
import styled from '@emotion/styled';

export const List = styled('ul')`
  list-style: none;
  padding: 0;
`;

This looks pretty weird to me because, not only are we writing styles for the <ul> element, but we’re defining that the component should render a <ul>, too. Combining both the markup and the styles in one place feels odd but I do like how simple it is. It just sort of messes with my mental model and the separation of concerns between HTML, CSS, and JavaScript.

In another component, we can import this <List> and use it like this:

import List from 'components/list';

<List>This is a list item.</List>

The styles we added to our list component will then be turned into a classname, like .oefioaueg, and then added to the <ul> element we defined in the component.

But we’re not done yet! With the list design, I needed to be able to render a <ul> and an <ol> with the same component. I also needed a version that allows me to place an icon within each list item. Just like this:

The cool (and also kind of weird) thing about Emotion is that we can use the as attribute to select which HTML element we’d like to render when we import our component. We can use this attribute to create our <ol> variant without having to make a custom type property or something. And that happens to look just like this:

<List>This will render a ul.</List>
<List as="ol">This will render an ol.</List>

That’s not just weird to me, right? It’s super neat, however, because it means that we don’t have to do any bizarro logic in the component itself just to change the markup.

It was at this point that I started to jot down what the perfect API for this component might look like though because then we can work our way back from there. This is what I imagined:

<List>
  <ListItem>Item 1</ListItem>
  <ListItem>Item 2</ListItem>
  <ListItem>Item 3</ListItem>
</List>

<List>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem>
</List>

<List as="ol">
  <ListItem>Item 1</ListItem>
  <ListItem>Item 2</ListItem>
  <ListItem>Item 3</ListItem>
</List>

So after making this sketch I knew we’d need two components, along with the ability to nest icon subcomponents within the <ListItem>. We can start like this:

import React from 'react';
import styled from '@emotion/styled';

export const List = styled('ul')`
  list-style: none;
  padding: 0;
  margin-bottom: 20px;

  ol& {
    counter-reset: numberedList;
  }
`;

That peculiar ol& syntax is how we tell emotion that these styles only apply to an element when it’s rendered as an <ol>. It’s often a good idea to just add a background: red; to this element to make sure your component is rendering things correctly.

Next up is our subcomponent, the <ListItem>. It’s important to note that at Sentry we also use TypeScript, so before we define our <ListItem> component, we’ll need to set our props up first:

type ListItemProps = {
  icon?: React.ReactNode;
  children?: string | React.ReactNode;
  className?: string;
};

Now we can add our <IconWrapper> component that will size an <Icon> component within the ListItem. If you remember from the example above, I wanted it to look something like this:

<List>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem>
</List>

That IconBusiness component is a preexisting component and we want to wrap it in a span so that we can style it. Thankfully, we’ll need just a tiny bit of CSS to align the icon properly with the text and the <IconWrapper> can handle all of that for us:

type ListItemProps = {
  icon?: React.ReactNode;
  children?: string | React.ReactNode;
  className?: string;
};

const IconWrapper = styled('span')`
  display: flex;
  margin-right: 15px;
  height: 16px;
  align-items: center;
`;

Once we’ve done this we can finally add our <ListItem> component beneath these two, although it is considerably more complex. We’ll need to add the props, then we can render the <IconWrapper> above when the icon prop exists, and render the icon component that’s passed into it as well. I’ve also added all the styles below so you can see how I’m styling each of these variants:

export const ListItem = styled(({icon, className, children}: ListItemProps) => (
  <li className={className}>
    {icon && (
      <IconWrapper>
        {icon}
      </IconWrapper>
    )}
    {children}
  </li>
))<ListItemProps>`
  display: flex;
  align-items: center;
  position: relative;
  padding-left: 34px;
  margin-bottom: 20px;
	
  /* Tiny circle and icon positioning */
  &:before,
	& > ${IconWrapper} {
    position: absolute;
    left: 0;
  }

  ul & {
    color: #aaa;
    /* This pseudo is the tiny circle for ul items */ 
    &:before {
      content: '';
      width: 6px;
      height: 6px;
      border-radius: 50%;
      margin-right: 15px;
      border: 1px solid #aaa;
      background-color: transparent;
      left: 5px;
      top: 10px;
    }
		
    /* Icon styles */
    ${p =>
      p.icon &&
      `
      span {
        top: 4px;
      }
      /* Removes tiny circle pseudo if icon is present */
      &:before {
        content: none;
      }
    `}
  }
  /* When the list is rendered as an <ol> */
  ol & {
    &:before {
      counter-increment: numberedList;
      content: counter(numberedList);
      top: 3px;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      width: 18px;
      height: 18px;
      font-size: 10px;
      font-weight: 600;
      border: 1px solid #aaa;
      border-radius: 50%;
      background-color: transparent;
      margin-right: 20px;
    }
  }
`;

And there you have it! A relatively simple <List> component built with Emotion. Although, after going through this exercise I’m still not sure that I like the syntax. I reckon it sort of makes the simple stuff really simple but the medium-sized components much more complicated than they should be. Plus, it could be pretty darn confusing to a newcomer and that worries me a bit.

But everything is a learning experience, I guess. Either way, I’m glad I had the opportunity to work on this tiny component because it taught me a few good things about TypeScript, React, and trying to make our styles somewhat readable.


How to Make a List Component with Emotion originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-make-a-list-component-with-emotion/feed/ 0 316298
Considerations for Creating a Card Component https://css-tricks.com/considerations-for-creating-a-card-component/ https://css-tricks.com/considerations-for-creating-a-card-component/#comments Mon, 02 Mar 2020 20:04:40 +0000 https://css-tricks.com/?p=303926 Even a simple Card component can be tricky to design an API around. What abstraction are you providing? How much control do you want to give? If you make it too flexible, you aren't providing much value. If you make it to rigid, you also aren't providing much value.


Considerations for Creating a Card Component originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Here’s a Card component in React:

const Card = props => {
  return(
    <div className="card">
      <h2>{props.title}</h2>
      <p>{props.content}</p>
    </div>
  )
}

It might be pretty useful! If you end up using this thing hundreds of times, now you have the ability to refactor a little bit of HTML across your app very easily. You already have that power in CSS because of the class name there, but now you have HTML control too. Feel it.

But wait. Maybe this is limiting… an <h2>? What if that really should have been an <h4> in some usages? What’s the approach there? Maybe an API of sorts?

const Card = props => {
  return(
    <div className="card">
      {props.type === "big" && <h2>{props.title}</h2>}
      {props.type !== "big" && <h4>{props.title}</h4>}
      <p>{props.content}</p>
    </div>
  )
}

Or maybe we force a level to be passed in?

const Card = props => {
  const HeaderTag = `h${props.level}`;
  return(
    <div className="card">
      <HeaderTag>{props.title}</HeaderTag>
      <p>{props.content}</p>
    </div>
  )
}

Or maybe that header is its own component?

And a forced paragraph tag wrapper around that content? That’s a little limiting, isn’t it? Maybe that should be a <div> so that it could take arbitrary HTML inside it, like multiple paragraphs.

const Card = props => {
  return(
    <div className="card">
      <WhateverHeader>{props.title}</WhateverHeader>
      <div>{props.content}</div>
    </div>
  )
}

Actually, why even ask for content with props? It’s probably easier to deal with a child component, especially if what is coming over is HTML.

const Card = props => {
  return(
    <div className="card">
      <WhateverHeader>{props.title}</WhateverHeader>
      {children}
    </div>
  )
}

There are more assumptions we could challenge too. Like card only for a class name… shouldn’t that be more flexible?

const Card = props => {
  const classes = `card ${props.className}`;
  return(
    <div className={classes}>
      <WhateverHeader>{props.title}</WhateverHeader>
      {children}
    </div>
  )
}

I’m still forcing card there. We could drop that so that it isn’t assumed, or build another aspect of the Card API providing a way to opt-out of it.

Even the <div> wrapper is presumptuous. Perhaps that tag name could be passed in so that you could make it into a <section> or <article> or whatever you want.

Maybe it’s better to assume nothing actually, making our card like this:

const Card = () => {
  return(
    <>
      {children}
    </>
  )
}

That way anything you want to change, you have the freedom to change. At least then it’s flexibility while being relaxed about it, rather than this kind of “flexibility”:

<Card
  parentTag="article"
  headerLevel="3"
  headerTitle="My Card"
  contentWrapper="div"
  cardVariation="extra-large"
  contentContent=""
  this=""
  little=""
  piggy=""
  went=""
  to=""
  market=""
/>

That kind of extreme-API-zying just happens sometimes when you’re grasping for control and flexibility at the same time.

A component model with no guidance can lead to over-componentization also, like perhaps:

const Card = props => {
  return(
    <CardWrapperTheme>
      <CardWrapper>
        <CardTitle />
        <CardContent />
        <CardFooter />
      </CardWrapper>
    </CardWrapperTheme>
  )
}

There might be perfectly good reasons to do that, or it might be the result of componentizing because it’s “free” and just feels like that’s how things are done in an architecture that supports it.

There is a balance. If a component is too strict, it runs the risk of that people won’t use them because they don’t give them what they need. And if they’re too loose, people might not use them because they don’t provide any value, and, even if they did use them, they don’t offer any cohesiveness.

I don’t have any answers here, I just find it fascinating.


Considerations for Creating a Card Component originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/considerations-for-creating-a-card-component/feed/ 6 303926
Third-Party Components at Their Best https://css-tricks.com/third-party-components-at-their-best/ https://css-tricks.com/third-party-components-at-their-best/#comments Thu, 16 Jan 2020 21:49:35 +0000 https://css-tricks.com/?p=300598 I’m a fan of the componentization of the web. I think it’s a very nice way to build a website at just about any scale (except, perhaps, the absolute most basic). There are no shortage of opinions about what makes …


Third-Party Components at Their Best originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I’m a fan of the componentization of the web. I think it’s a very nice way to build a website at just about any scale (except, perhaps, the absolute most basic). There are no shortage of opinions about what makes a good component, but say we scope that to third-party for a moment. That is, components that you just use, rather than components that you build yourself as part of your site’s unique setup.

What makes a third-party component good? My favorite attribute of a third-party component is when it takes something hard and makes it easy. Particularly things that recognize and properly handle nuances, or things that you might not even know enough about to get right.

Perhaps you use some component that does pop-up contextual menus for you. It might perform browser edge detection, such as ensuring the menu never appears cut off or off-screen. That’s a tricky little bit of programming that you might not get right if you did it yourself — or even forget to do.

I think of the <Link /> component that React Router has or what’s used on Gatsby sites. It automatically injects aria-current="page" for you on the links when you’re on that page. You can and probably should use that for a styling hook! And you probably would have forgotten to program that if you were handling your own links.

In that same vein, Reach UI Tabs have rigorous accessibility baked into them that you probably wouldn’t get right if you hand-rolled them. This React image component does all sorts of stuff that is relatively difficult to pull off with images, like the complex responsive images syntax, lazy loading, placeholders, etc. This is, in a sense, handing you best practices for “free.”

Here’s a table library that doesn’t even touch UI for you, and instead focuses on other needs you’re likely to have with tables, which is another fascinating approach.

Anyway! Here’s what y’all said when I was asking about this. What makes a third-party component awesome? What do the best of them do? (besides the obvious, like good docs and good accessibility)? Some of these might be at-odds. I’m just listing what people said they like.

  • Plug-and-play. It should “just work” with minimal config.
  • Lots of editable demos
  • Highly configurable
  • “White label” styling. Don’t bring too strong of design choices.
  • Styled via regular CSS so you can BYO own styling tools
  • Fast
  • Small
  • Is installable via a package manager
  • Can be manually instantiated
  • Can be given a DOM node where it can go
  • Follows a useful versioning scheme
  • Is manintained, particularly for security
  • Has a public roadmap
  • Is framework-agnostic
  • Doesn’t have other dependencies
  • Uses intuitive naming conventions
  • Supports internationalization
  • Has lots of tests

Anything you’d add to that list?


Third-Party Components at Their Best originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/third-party-components-at-their-best/feed/ 3 300598
Components, yo. https://css-tricks.com/components-yo/ https://css-tricks.com/components-yo/#comments Fri, 14 Jun 2019 13:57:15 +0000 http://css-tricks.com/?p=289212 I see VuePress just went 1.0. Explained simply, it’s a static site generator based on Vue. But of course, you work in Vue, which means you work in components.

All the modern JavaScript frameworks are component-based. Even …


Components, yo. originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I see VuePress just went 1.0. Explained simply, it’s a static site generator based on Vue. But of course, you work in Vue, which means you work in components.

All the modern JavaScript frameworks are component-based. Even when they disagree with each other about specific things (like how Svelte requires compilation), they all seem to agree on the model of working in components. React is all components. A popular static site generator for React is Next.js. The Vue version of that is Nuxt.js.

Then there is Gatsby which is all React. (Listen to our latest ShopTalk Show as we discuss it.) Gridsome seems like the most 1-to-1 comparison in Vue-land, the notable comparison being how they both are designed to suck in data from any source. Components though, of course. I’m not sure there is a flagship Angular-based static site generator, but they are out there, and Angular is components all the way down.

Components are so ubiquitous that perhaps you don’t even think about it anymore. But you might feel it, particularly if you jump back and forth between projects that aren’t component-driven. WordPress development, generally, I feel, isn’t component driven. Sure, you’ve got your header.php and footer.php files and such. You can break those apart however you want, but it’s rather ad-hoc. You aren’t explicitly building components and feeding those components local data and testing them as such. (You can get a lot closer with something like Timber.)

Building front-ends out of server-side code is absolutely fine. Server-side rendering is rife with advantages. But server-side languages don’t seem to have embraced components the way JavaScript has. And since everyone seems to like components (front-end devs obviously love it, designers think that way anyway, back-end devs understand it…) it’s no surprise to me to see this surge of beloved projects build server-side (or build-time) generated sites from JavaScript, simply because it’s component-based and components are just a good idea.


Components, yo. originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/components-yo/feed/ 6 289212
Compound Components in React Using the Context API https://css-tricks.com/compound-components-in-react-using-the-context-api/ https://css-tricks.com/compound-components-in-react-using-the-context-api/#comments Fri, 07 Dec 2018 00:59:15 +0000 http://css-tricks.com/?p=278638 Compound components in React allow you to create components with some form of connected state that’s managed amongst themselves. A good example is the Form component in Semantic UI React.

To see how we can implement compound components in …


Compound Components in React Using the Context API originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Compound components in React allow you to create components with some form of connected state that’s managed amongst themselves. A good example is the Form component in Semantic UI React.

To see how we can implement compound components in a real-life React application, we’ll build a compound (multi-part) form for login and sign up. The state will be saved in the form component and we’ll put React’s Context AP to use to pass that state and the method from the Context Provider to the component that needs them. The component that needs them? It will become a subscriber to Context Consumers.

Here’s what we’re building:

See the Pen React Compound Component by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Here’s a rough outline that shows how the following steps fit together:

Form is the provider with state, Form Panel is the consumer receiving state, Panel displays the panel based on the state, and Signup and Login render the form views in the Panel.

Before treading any further, you may want to brush up on the React Context API if you haven’t already. Neal Fennimore demonstrates the concept in this post and my primer on it is worth checking out as well.

Step 1: Creating context

First, let’s initialize a new context using the React Context API.

const FormContext = React.createContext({});
const FormProvider = FormContext.Provider;
const FormConsumer = FormContext.Consumer;

The provider, FormProvider, will hold the application state, making it available to components that subscribe to FormConsumer.

Step 2: Implement provider

One panel contains the form to log in and the other contains the form to sign up. In the provider, we want to declare the state, which determines the active panel, i.e. the form currently in display. We’ll also create a method to switch from one panel to another when a heading is clicked.

class Form extends React.Component {
  state = {
    activePanel: "login"
  };

  render() {
    return (
      <React.Fragment>
        <FormProvider
          value={{
            activePanel: this.state.activePanel,
            actions: {
              handlePanelSwitch: newPanel => {
                this.setState({
                  activePanel: newPanel
                });
              }
            }
          }}
        >
          {this.props.children}
        </FormProvider>
      </React.Fragment>
    );
  }
}

By default, the login panel will be shown to the user. When the signup panel is clicked, we want to make it the active panel by setting the state of activePanel to signup using the method handlePanelSwitch().

Step 3: Implement Consumers

We’ll use FormConsumer to make context available to the components that subscribe to it. That means the FormPanel component that handles displaying panels will look like this:

const FormPanel = props => {
  return (
    <FormConsumer>
      {({ activePanel }) =>
        activePanel === props.isActive ? props.children : null
      }
    </FormConsumer>
  );
};

And the Panel component will look like this:

const Panel = props => (
  <FormConsumer>
    {({ actions }) => {
      return (
        <div onClick={() => actions.switchPanel(props.id)}>
          {props.children}
        </div>
      );
    }}
  </FormConsumer>
);

To understand what is happening, let’s understand the approach here. The login and signup panels will have unique IDs that get passed via props to the Panel component. When a panel is selected, we get the ID and and use it to set activePanel to swap forms. The FormPanel component also receives the name of the panel via the isActive prop and we then check to see if the returned value is true. If it is, then the panel is rendered!

To get the full context, here is how the App component looks:

const App = () => {
  return (
    <div className="form-wrap">
      <Form>
        <div className="tabs">
          <Panel id="login">
            <h2 className="login-tab">Login</h2>
          </Panel>
          <Panel id="signup">
            <h2 className="signup-tab">Sign Up</h2>
          </Panel>
        </div>

        <FormPanel isActive="login">
          <Login />
        </FormPanel>

        <FormPanel isActive="signup">
          <SignUp />
        </FormPanel>
      </Form>
    </div>
  );
};

You can see how the components are composed when activePanel matches isActive (which is supposed to return true). The component is rendered under those conditions.

With that done, the Login component looks like this:

const Login = () => {
  return (
    <React.Fragment>
      <div id="login-tab-content">
        <form className="login-form" action="" method="post">
          <input
            type="text"
            className="input"
            id="user_login"
            placeholder="Email or Username"
          />
          <input
            type="password"
            className="input"
            id="user_pass"
            placeholder="Password"
          />
          <input type="checkbox" className="checkbox" id="remember_me" />
          <label htmlFor="remember_me">Remember me</label>

          <input type="submit" className="button" value="Login" />
        </form>
      </div>
    </React.Fragment>
  );
};

And the SignUp component:

const SignUp = () => {
  return (
    <React.Fragment>
      <div id="signup-tab-content" className="active tabs-content">
        <form className="signup-form" action="" method="post">
          <input
            type="email"
            className="input"
            id="user_email"
            placeholder="Email"
          />
          <input
            type="text"
            className="input"
            id="user_name"
            placeholder="Username"
          />
          <input
            type="password"
            className="input"
            id="user_pass"
            placeholder="Password"
          />
          <input type="submit" className="button" value="Sign Up" />
        </form>
      </div>
    </React.Fragment>
  );
};

Get it? Got it? Good!

You can use this pattern anytime you have components in your React application that need to share implicit state. You can also build compound components using React.cloneElement().

References


Compound Components in React Using the Context API originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/compound-components-in-react-using-the-context-api/feed/ 3 278638
Prototyping in the Browser https://css-tricks.com/prototyping-in-the-browser/ https://css-tricks.com/prototyping-in-the-browser/#comments Thu, 05 Jul 2018 14:04:40 +0000 http://css-tricks.com/?p=272834 Prototyping animations and interactions is vital for a number of reasons: they can make your interface feel deceptively fast, they can help focus the user on a specific task, and they can provide a better sense of the current state …


Prototyping in the Browser originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Prototyping animations and interactions is vital for a number of reasons: they can make your interface feel deceptively fast, they can help focus the user on a specific task, and they can provide a better sense of the current state of your application. Is data being loaded? Is something now unclickable? How long do they have to wait until they can perform an action?

At Gusto, I’ve been working on a lot of tiny interaction details and prototypes lately for these very reasons — sadly there’s not much that I can show you all in detail just yet. But, I think the process of how I’m doing this is far more interesting than what I’m actually working on so that’s what I’m going to share here.

The problem I’ve faced with prototyping animations comes down to the tools because they ultimately feel restrictive to me. Whenever I’ve experimented with them in the past, I feel like one hand is always tied behind my back. It could very well be that I’m not using them correctly, but I feel that none of them really mimic browser behavior quite right and, although I’ve been fascinated to watch tools like Framer, Marvel, Zeplin and Principle gain traction, I don’t think they’re for me. At least not yet anyway.

In my opinion, the best way to prototype a lot of interactions is still with plain ol’ HTML, CSS and (gasp!) a sprinkle of jQuery when I need it.

How I’m prototyping today

With my recent project, I decided to invest a little bit of time into CodePen to help our design team quickly prototype things. Here’s what I built:

See the Pen Gusto Prototype: Header by Robin Rendle (@robinrendle) on CodePen.

No really, that’s it. It’s just a simple prototype that looks like our app.

It’s a pen with all the HTML for the app navigation — and it contains all the CSS, too. This way, a designer at Gusto can fork that pen and start writing HTML and CSS to experiment with a specific bit of UI without all the added pressure of having to write production code. It’s important to note that this prototype isn’t for figuring out UX, as tools like Figma and Sketch do a much better job of that. Prototypes like this are mostly useful when figuring out finicky UI interactions like tables, spreadsheets and dropdowns — components that can get complicated real quick.

To make this prototype, I simply copied and pasted all of the HTML and CSS from our web app into a new pen. (In the future, we should probably have a system where we’re always prototyping with the latest up-to-date code from our app but, for now, I think this is a fine starting point.) This pen contains all of the menu and navigation items we need to make it look like the Gusto app. We have a a separate pen for the footer which closes off all the divs that we open up in the header. Lastly, we have one more pen that imports those two other pens like so:

<!-- HEADER -->
[[[http://codepen.io/robinrendle/pen/481a88853820067752e28cdb479c91f3]]]

<!-- HTML GOES HERE -->
<h1>App Prototype</h1>
<p>You can fork this pen and write all the HTML and CSS you need to prototype interactions and explore ideas right here in your browser.</p>

<!-- FOOTER -->
[[[http://codepen.io/robinrendle/pen/0dcaa93954f06f4d03b7e23e8ea54cac]]]

See the Pen Gusto Prototype: Full by Robin Rendle (@robinrendle) on CodePen.

What’s that weird syntax with all the [[[ ]]]? That’s the HTML Include syntax for CodePen. If we put the URL of a pen in between those brackets, CodePen will fetch the code from that pen and place it straight into this new pen. That’s it! Pretty sweet, right?

To wrap things up, I wanted to make a couple of notes about what I’ve learned with this new setup.

Lesson 1: Prototypes should be super easy to share

Ideally, prototypes are easy to share with other designers and engineers, exceptionally fast to get setup, and don’t require prior training or expertise — and Codepen is perfect for that. I like this setup for a bunch of reasons. For one, we don’t have to teach designers CoffeeScript like we have to with Framer and we don’t have to run React or Vue workshops to get them up and running with a complex prototyping app.

Yes, folks do need to learn how HTML and CSS works to make prototypes like this but I think learning the very basics of those two languages is vital for a designer working on the web anyway.

Lesson 2: Bad code is a-okay

Here’s another thing I recently learned while doing prototyping: bad code is okay at this stage. In fact, we should be writing terrible, unforgivable code when prototyping in the browser. We ought to write the sort of CSS and HTML that would keep Harry Roberts up at night because clean, maintainable code gets in the way of the design process when the focus should be on iterating as quickly as possible.

To be quite honest, I don’t care about front-end best practices when I’m making these prototypes — I don’t think about BEM, semantic HTML or even performance. Oh, and I certainly don’t care about the most proficient way to render a React thingamijig.

As long as we ditch all of that prototype code and start from scratch later, and as long as there’s step to break the design into components and ensure that those lego pieces are maintainable, well documented, and highly performant in our production environment, then I believe that writing bad should not only be allowed, but actively encouraged.

This leads to my final takeaway.

Lesson 3: Designers and developers should always translate their code

I reckon that the first time a designer and/or front-end developer writes code, it should never be in a production environment. Having the leeway and freedom to go crazy with the code in a safe environment focuses your attention on the design and making it compatible with a browser’s constraints. After this, you can think about grooming the code from a hot, steaming heap of garbage into lovely, squeaky-clean, production-ready poetry. Translating the static mockups into an interactive prototype is the first step, but it’s vital to have a next step to enforce your code standards.

Does your app use BEM? How should you abstract each of these components into separate files? What do you call all of these new components that you’re introducing into the design system?

I believe all of these questions are easier to answer once you have that interactive prototype. And I would highly recommend that designers and front-end engineers alike experiment making little tools like this. It might feel a little odd at first, but I promise that it will produce much better work in the long run.


Prototyping in the Browser originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/prototyping-in-the-browser/feed/ 2 272834
One-Offs https://css-tricks.com/one-offs/ https://css-tricks.com/one-offs/#comments Mon, 02 Jul 2018 15:26:50 +0000 http://css-tricks.com/?p=272961 There is this sentiment that you don’t design the homepage of a site first. For most sites, it’s an anomaly. It’s unlike any other page and not something to base the patterns you use for the rest of the site …


One-Offs originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There is this sentiment that you don’t design the homepage of a site first. For most sites, it’s an anomaly. It’s unlike any other page and not something to base the patterns you use for the rest of the site or help inform other pages.

You might call it a one-off.1

One-offs are OK! A world without one-offs is very boring. But a site chock-full of one-offs leads to familiar problems: inconsistency and non-reusable CSS that leads to bloating and maintainers that don’t really know what’s used and what isn’t.

What are we to do?

Brad Frost thought about this recently with his article Where to put one-off components?:

It’s quite likely that individual applications contain components that fit the very real needs of a specific application (think calculators, holiday-season parallax hero units, context-specific interactive maps, and so on), but may not be able to be immediately (or ever) abstracted into a generic, reusable component that’s included in the design system.

And Ethan Marcotte:

Here’s an example: let’s say you’ve designed a splashy-looking hero image. (Nice work, by the way. It looks very splashy.) Let’s also say its scope is fairly limited: perhaps it’s being used on a small number of pages, or it’s tied to one specific section of your website. In other words, it’s generic enough to be a pattern, but it’s not a widely-used one.

As you might have guessed, this is a dangerous place for a pattern to be. If a pattern feels a little idiosyncratic, that should always prompt us to ask: should your team keep the pattern, or remove it?

Thing is, there’s no single, easy answer to that question. Every pattern is different, and each pattern’s value is variable. Maybe we’ll drop that pattern altogether; maybe we’ll combine it with another, similar pattern

Brad connected the idea of a one-off to Harry Robert’s shame.css concept:

The idea of shame.css is that you have a totally new stylesheet reserved just for your hacky code. The code you have to write to get the release out on time, but the code that makes you ashamed.

But maybe a CSS file like that doesn’t need to be reserved just for “hacks” or shamefully quickly-written fixes (hey, it’s better than inline styles), but also for one-offs.

From a developer-in-charge-of-styling perspective, it’s interesting to consider how different styling methologies come into play here. For those travelling the atomic CSS road, in a sense, everything is a one-off. You might still have a pattern that is visually or behaviorly a one-off because of how you’ve built it, but not because it uses a different set of CSS. Callum Jefferies on his experience with atomic CSS:

I no longer had to think about how to organise my CSS. I didn’t have to think about what to name my ‘components’, where to draw the line between one component and another, what should live where, and crucially, how to refactor things when new requirements came in.

So, too, with the 36 flavors of CSS-in-JS. When your styles are attached to your components, all components are one-offs. Stop using that component, stop using those styles.


1 Apparently one-off is a fairly new expression:

As William Safire observed in a 2007 On Language column, one-off meaning “something unique” is a British expression that has been creeping into American speech and writing in recent years. And as with other Briticisms that impinge on these shores (gone missing comes to mind), the idiomatic origins of one-off are mostly lost on American ears.


One-Offs originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/one-offs/feed/ 3 272961
How to Create a Component Library From SVG Illustrations https://css-tricks.com/how-to-create-a-component-library-from-svg-illustrations/ https://css-tricks.com/how-to-create-a-component-library-from-svg-illustrations/#comments Thu, 12 Apr 2018 14:01:53 +0000 http://css-tricks.com/?p=269385 I’ve recently published my first ever open source npm package! It makes SVG illustrations from unDraw into customizable React components.

Here’s a GIF that shows what I mean:

What’s unDraw?

While unDraw is still fairly new, its open source …


How to Create a Component Library From SVG Illustrations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I’ve recently published my first ever open source npm package! It makes SVG illustrations from unDraw into customizable React components.

Here’s a GIF that shows what I mean:

An animated GIF of an illustration of a  man sitting behind a laptop computer with the colors of the illustration changing.

What’s unDraw?

unDraw is a collection of MIT licensed illustrations for every project you can imagine and create. It’s a constantly updated collection of beautiful SVG images that you can use completely free and without attribution.

While unDraw is still fairly new, its open source nature means that it’s being used by a range of products already. Here it is on the newly launched design mentoring site called MentorOla, alongside a language site I’ve been working on myself called Little Lingua:

A side-by-side image showing two websites using unDraw SVG illustrations.
Left: MentorOla by Marc Andrew, Right: Little Lingua]

While using React to build the Little Lingua website, I discovered that converting SVGs into React components made them much more manageable and even more customizable as illustrations.

Because of this usefulness, I wanted to release a library of unDraw React components as an open source npm package to bring the beauty of unDraw to the largest of JavaScript audiences in a simple way:

Framework Nov. 2016 Oct. 2017 % Change
React 2,564,601 7,040,410 174.5%
Angular 1,289,953 2,168,899 68.1%
Backbone 663,610 837,372 31.6%
Angular.js 616,135 1,081,796 75.6%
Vue 6,231 874,424 13,933.%

Source: JavaScript Frameworks by the Numbers

John Hannah, who authored the the JavaScript Frameworks by the Numbers report:

React absolutely dominates. It’s by far the most downloaded according to these numbers…

A lot of inspiration to put this package together came from Miuki Miu’s project React Kawaii, where she did largely the same thing, and a bit more:

Screenshots from the React Kawaii website, including illustrations that inspired the unDraw project.

Her article outlines the overall concept of SVGs as React Components, and here I will go into more of the details involved in my own process including putting together Styleguideist documentation. Here’s everything that will be covered:

  1. How to convert SVG illustrations into customisable React Components
  2. How to use Styleguideist to produce simple, interactive documentation
  3. How to release an npm package (since it was my first time doing this)

1. SVG Illustrations as React Components

When you visit unDraw, it’s currently possible to customize one primary color of each SVG illustration right from the website:

The SVG download you get when you grab the image is pretty tedious to customize any further, as there’s many color values to change. If that SVG is converted to a React component though, it becomes really easy! Compare the two:

unDraw designer SVG vs. React Component

Essentially, the SVG is held within a React component, which is very simple to interface with. You just pass properties to the Component (e.g. skinColor/hairColor/primaryColor), which then sprinkles them into the SVG.

You can do this in just three steps:

Convert the SVG to JSX

There are some awesome tools out there to convert SVGs into the JSX code that’s used in a React component’s render() method. The first one I used was the first one I came across: SVG to JSX—it was also the first Google search result 😉. As it says on the tin, any JSX code is generated from any SVG you paste in:

SVG to JSX converter by Balaj Marius

Once you’ve got your JSX, paste it into your React component like so:

import React from 'react';
import PropTypes from 'prop-types';
const UndrawDesigner = props => (
<svg id='780c6f38–12e9–4526–8343–95ef18389740' dataName='Layer 1' xmlns='http://www.w3.org/2000/svg'>
  // all your svg code
</svg>
);
export default UndrawDesigner;

That’s it! Now you can use this as a component by dropping this into your code:

<UndrawDesigner/>

Right now, you’ll be stuck with the default colors of your SVG. Let’s make those colors easy to change:

Make it Customizable With Props

We can use the benefits of React to make the illustration customisable by adding *props* as placeholders that are used to fill the *color attributes* of the SVG/JSX in your component:

<svg xmlns='http://www.w3.org/2000/svg'>

  <path fill={props.hairColor} d='...' />
  <path fill={props.hairColor} d='...' />
  
  <ellipse fill={props.skinColor} cx='...' cy='...' rx='...' ry='...' />
  <ellipse fill={props.skinColor} cx='...' cy='...' rx='...' ry='...' />
  
  <!-- etc -->

</svg>

To make sure you’re replacing the right fill attributes, you can open the SVG in your browser, and identify colors using your browser’s inspector tools:

You can see the color here is rgb(226,189,149) . Convert that to a hex code. There’s many ways to do this, one is searching “colorpicker” in Google :

Since a single color is often used in numerous places in an SVG illustration (e.g. left hand, right hand, face will be the same), there will be many places a color needs replacing. To do it quickly, grab the HEX code, and do a find-and-replace in your component, replacing the color attribute with your prop name, e.g. {props.skinColor} .

Do this with as many colors/elements of your SVG as you’d like to make customizable, ensuring your props are named so that they’re easy for other people to understand and use.

Add PropType definitions and Default Colors

Once you’ve finished adding your props, it’s good practice to define them as propTypes. This will also help when we make awesome documentation for our components. Add them like so (make sure you’ve got prop-types installed in your project):

UndrawDesigner.propTypes = {
/**
* Hex color
*/
skinColor: PropTypes.string,
/**
* Hex color
*/
hairColor: PropTypes.string,
/**
* Hex color
*/
primaryColor: PropTypes.string,
};

Finish up your component by defining some default colors, right before the export statement. This ensures a fallback color will be used if no props are passed to the component:

UndrawDesigner.defaultProps = {
  skinColor: '#e2bd95',
  primaryColor:'#6c68fb',
  hairColor:'#222'
};
export default UndrawDesigner;

After doing this, your component will be ready to accept values for each of the attributes defined. For example, in UndrawDesigner, we can make a little gray human by passing in various types of gray for skin and hair. Nice and simple:

It’s that much simpler, really. If you want to go beyond changing colors, read Miuki Miu’s article, where she cleverly adds smaller common components that are used as facial expressions across larger components:

2. Making the Style Guide

To make the React illustrations more useful to everyone, it’s possible to create a living style guide of the components using React Styleguidist. It’s not much extra work, either.

Because of how Stylguidist works with React, it’s really straightforward to create documentation from the components we have. Styleguidist requires two main things to generate documentation from our components:

  1. Clear PropType definitions
  2. Component examples

We’ve already taken care of the first one in the previous section. The comments above each PropType definition is also important, as it gets displayed in the end documentation:

Adding component examples is also straightforward—add a Readme.md to the folder of your component with an example of how it would be used. The contents may look something like this:

// UndrawResponsive example
```js
<UndrawResponsive
height='250px'
primaryColor='#6c68fb'
accentColor='#43d1a0'
/>
```

You can find out more in the Styleguidist documentation.

Once you’ve got those two in place, installing and running Styleguidist will create the documentation like magic. Follow the instructions here to install and run it.

3. Releasing the npm package

At this stage, I had a folder of React components with unDraw illustrations, but it’s useless to any other project. Here are the steps I took to turn them into an npm module:

  1. Create a brand new React project using Facebook’s create-react-app
  2. Copy over the react components you’d like to release an npm module into src/node_modules/components of your creat-react-app project
  3. Follow these steps outlined by Pavel Lokhmakov

Finally, to publish your module, create an npm account and follow these two short videos of the npm documentation:

  1. How to create Node.js modules
  2. How to publish and update a package

That’s it! There are over 100 unDraw illustrations by Katerina Limpitsouni on unDraw. At the moment, I’ve only added a handful of those to the unDraw npm package, but will be adding more each week.

Check out the GitHub repository here. I’ll also be releasing the code for the LittleLingua soon, the website that makes use of this unDraw npm package. It’s built with unDraw’s production-ready MIT licensed theme, called evie, which I’ve also converted into React components.

To learn more about transforming SVG illustrations into components, check out Elizabet Oliveira’s talk about her side project, React Kawaii which was also nominated as “Fun Side Project of the Year” at the React Amsterdam Open Source Awards:


How to Create a Component Library From SVG Illustrations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-create-a-component-library-from-svg-illustrations/feed/ 2 269385