At GitLab, the front-end department has dealt with too many CSS issues. To take a stand against these issues, we’ve declared to not write as little CSS as possible. We’ve focused on writing utility classes to reduce these CSS conflicts.
As part of this, the talk of using tailwindcss came up. Unfortunately, we are on bootstrap and stuck in the bootstrap world, and I personally assume that there would be a lot of collision, either through class naming or specificity, or something else. It made me take a long hard look at my dependency on Jekyll themes, and wondered why I never bothered writing this up myself.
Now, there’s nothing wrong with using a theme, and Lanyon is a beautiful theme, but I can’t avoid learning a new set of tools (especially if I don’t have to write all the CSS utilities myself from scratch). This is a little post on the set-up.
Installing Everything
Most of my immediate research showed a lot of starter projects. The problem, I
found, is that they added gulp
on top of the build chain, and I didn’t think
this was very necessary. A little more digging, and some dots started to
connect.
PostCSS
Tailwind is actually a PostCSS plugin. PostCSS, it turns out, is akin to Babel but for CSS, and there’s a Jekyll plugin for PostCSS. Now we’re cooking!
After installing PostCSS and tailwindcss from NPM and some quick configuration
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
],
};
// tailwind.config.js
module.exports = {
theme: {
extend: {}
},
variants: {},
plugins: []
};
# _config.yml
plugins:
- jekyll-postcss
and a minimal tailwind file
---
---
/* note the required empty front-matter */
@tailwind base;
@tailwind components;
@tailwind utilities;
and we can use tailwind as much as we like!
Styling
Styling went pretty smoothly. Delete all the CSS (but syntax highlighting), add the utilities, style the text… wait, why do my posts look so terrible?
Kramdown and Tailwind
Kramdown is the default markdown utility used by Jekyll, but doesn’t add any classes or styles to our text. A blessing and a curse, as not only does tailwind not add any styles either, but actively stripes out any default styling, leaving my posts in a jumbled mess. There was no way to inject classes into Kramdown’s output either. Effectively stuck.
PostCSS to the rescue
PostCSS is built around using future CSS syntax today, and a plugin available to
use is postcss-nesting
, which implements the draft spec. Now we
could using nesting to easily style our post text. A little more configuration:
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('postcss-nesting'),
],
};
and some styling:
a {
@apply text-teal-600 font-semibold;
&:hover {
@apply underline;
}
}
.post {
& p {
@apply mt-4;
}
& h2 {
@apply text-3xl mt-8;
}
& h3 {
@apply text-2xl mt-8;
}
& h4 {
@apply text-xl mt-8;
}
& div.highlighter-rouge {
@apply rounded p-2 my-3 overflow-x-auto;
}
& code.highlighter-rouge {
@apply rounded p-1 whitespace-no-wrap;
}
& ul {
@apply list-disc list-outside mt-2;
}
& ol {
@apply list-decimal list-outside mt-2;
}
& li {
@apply ml-4 my-2;
}
& .badge {
@apply inline m-0 align-middle;
}
}
and now my posts are beautiful again!
What other fun features can we add
I mean I always liked dark modes…
DARK MODE
I felt like adding a dark mode, but I didn’t want to force all users to live in the darkness. After all, I’m sure there are some people out there who like light themes, right? First, setting up some dark variants using the wonderful tailwindcss-dark-mode.
module.exports = {
theme: {
extend: {}
},
variants: {
backgroundColor: ['hover', 'dark', 'dark-hover', 'dark-group-hover'],
borderColor: ['hover', 'dark', 'dark-focus', 'dark-focus-within'],
textColor: ['hover', 'dark', 'dark-hover', 'dark-active'],
},
plugins: [
require('tailwindcss-dark-mode')(),
]
};
Then, start sprinkling in the dark variants in our HTML:
<body class="text-gray-900 bg-gray-100 dark:text-gray-100 dark:bg-gray-900">
<!-- ... --->
</body>
and all we have to do is set the mode-dark
class on our root <html>
to turn
it on!
Toggling Dark Mode
Browsers can know a lot about our preferences and location, like which theme we prefer and the current time where we are. Building something around it to intelligently toggle dark mode should be pretty easy:
document.addEventListener( 'DOMContentLoaded', function () {
const root = document.querySelector(':root')
let isDark = false;
let isLight = false;
let isNotSpecied = false;
let setDark = JSON.parse(window.localStorage.getItem('dark'));
const supported = typeof window.matchMedia === 'function';
if (setDark == null) {
if (supported) {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isLight = window.matchMedia('(prefers-color-scheme: light)').matches;
isNotSpecified = window.matchMedia('(prefers-color-scheme: no-preference)').matches;
if (isDark) {
setDark = true;
}
if (isLight) {
setDark = false;
}
}
if (!supported || isNotSpecified) {
const hour = (new Date()).getHours();
if (hour < 5 || hour > 20) {
setDark = true;
} else {
setDark = false;
}
}
}
root.classList.toggle('mode-dark', setDark);
window.localStorage.setItem('dark', setDark);
}, false);
First, we try to check if the user prefers a specific theme, then we try to see if it’s (roughly) nighttime where the user is. If the user explicitly prefers a theme, we go with that. If they don’t or they don’t support the technology, we see if it’s night or day.
You may notice a sun or moon in the top left corner. This allows the user to toggle the theme to their heart’s content. I won’t put the implementation here.
Conclusion
Turns out you don’t need gulp
to put Jekyll and Tailwind together, and if
you go the PostCSS route, you have access to a whole ecosystem of future CSS
improvements as well.
On top of this, I’ve also added autoprefixer
to the build to ensure
newer styles work regardless of browser choice, but there are many others
to look through. Who knows what I’ll find digging through this list and add
to this blog.