javascriptcsstailwindcss

Exploring Tailwind-in-JS: Tailwind CSS on demand

A while ago I was exploring ways to adopt Tailwind CSS to style content that I was not responsible for. A good example of this is content coming from a database. For context, Tailwind CSS builds the styles needed for an application based on the folders and file patterns the developer specifies in their config file during the build. If the markup is dynamically rendered, the build tool doesn’t know what to include in the final bundle.

Other than content coming from a database, this is also a problem site generators face. While researching different products, I came accross Versoly, which uses Tailwind CSS under the hood. That is how I met Twind, according to them “The smallest, fastest, most feature complete Tailwind-in-JS solution in existence”. Converting Tailwind CSS classes into CSS at runtime was exactly what I needed for the example above.

Take the following example: the CSS generated for <button class="bg-sky-500 hover:bg-sky-700"> Save changes </button> will be:

/* @layer base */
 
*,
::before,
::after {
  box-sizing: border-box;
  border-width: 0;
  border-style: solid;
  border-color: #e5e7eb;
}
::before,
::after {
  --tw-content: "";
}
html {
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -moz-tab-size: 4;
  -moz-tab-size: 4;
  tab-size: 4;
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  font-feature-settings: normal;
}
body {
  margin: 0;
  line-height: inherit;
}
hr {
  height: 0;
  color: inherit;
  border-top-width: 1px;
}
abbr:where([title]) {
  -webkit-text-decoration: underline dotted;
  text-decoration: underline dotted;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: inherit;
  font-weight: inherit;
}
a {
  color: inherit;
  -webkit-text-decoration: inherit;
  text-decoration: inherit;
}
b,
strong {
  font-weight: bolder;
}
code,
kbd,
samp,
pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", monospace;
  font-feature-settings: normal;
  font-size: 1em;
}
small {
  font-size: 80%;
}
sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}
sub {
  bottom: -0.25em;
}
sup {
  top: -0.5em;
}
table {
  text-indent: 0;
  border-color: inherit;
  border-collapse: collapse;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: inherit;
  color: inherit;
  margin: 0;
  padding: 0;
}
button,
select {
  text-transform: none;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: button;
  background-color: transparent;
  background-image: none;
}
:-moz-focusring {
  outline: auto;
}
:-moz-ui-invalid {
  box-shadow: none;
}
progress {
  vertical-align: baseline;
}
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
  height: auto;
}
[type="search"] {
  -webkit-appearance: textfield;
  outline-offset: -2px;
}
::-webkit-search-decoration {
  -webkit-appearance: none;
}
::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit;
}
summary {
  display: list-item;
}
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
  margin: 0;
}
fieldset {
  margin: 0;
  padding: 0;
}
legend {
  padding: 0;
}
ol,
ul,
menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
textarea {
  resize: vertical;
}
input::placeholder,
textarea::placeholder {
  opacity: 1;
  color: #9ca3af;
}
button,
[role="button"] {
  cursor: pointer;
}
:disabled {
  cursor: default;
}
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
  display: block;
  vertical-align: middle;
}
img,
video {
  max-width: 100%;
  height: auto;
}
[hidden] {
  display: none;
}
 
/* @layer utilities */
 
.bg-sky-500 {
  --tw-bg-opacity: 1;
  background-color: rgba(14, 165, 233, var(--tw-bg-opacity));
}
.hover\:bg-sky-700:hover {
  --tw-bg-opacity: 1;
  background-color: rgba(3, 105, 161, var(--tw-bg-opacity));
}

The first part of the file (lines 1-200) includes the base reset that Tailwind CSS ships to the browser and the second part (lines 202-211) includes the styles from the HTML markup I had. Here is an interactive example:

<h1 class="text-3xl text-indigo-800">Hello world</h1>

<script type="module">
  // The shim script process the html on the page
  import twind from 'https://cdn.skypack.dev/twind/shim';

  // The observer accordingly builds the styles based on the node
  // (document.body in this case)
  import twindObserver from 'https://cdn.skypack.dev/twind/observe';

  twindObserver.observe(document.body);
</script>

In the example above, if you change text-3xl to text-6xl, you will see the Hello world size increase instantly. This is obviously a simple example but it gives you an idea of what Twind is capable of. Their documentation website is very complete and it shows how to use custom Tailwind CSS configs (theming for examples) and how to integrate with your framework.

Interactions

Webmentions

Like this content? Buy me a coffeeor share around:

0 Like

0 Reply & Share