Tuesday, January 30, 2024
Migration twind CSS to tailwind CSS
Upgrading to Deno Fresh 1.6 with Tailwind CSS Integration
The recent Fresh 1.6 release introduced an exciting update for developers: the addition of a Tailwind CSS plugin. This change, while seemingly minor, signifies a substantial shift in how styles can be managed and applied within Deno Fresh projects. Transitioning to this new setup took me several hours, but the benefits of using Tailwind CSS with Fresh are undeniable. Here's a comprehensive guide on how to make this upgrade smoothly.
Key Changes in the Upgrade Process
twind
to tailwind
Switching from The first step involves updating the deno.json
configuration. Replace the twind
import with the new tailwindcss
import as follows:
Before:
{ "imports": { "twind": "https://esm.sh/twind@0.16.19", "twind/": "https://esm.sh/twind@0.16.19/" } }
After:
{ "imports": { "tailwindcss": "npm:tailwindcss@3.3.5", "tailwindcss/": "npm:/tailwindcss@3.3.5/", "tailwindcss/plugin": "npm:/tailwindcss@3.3.5/plugin.js" } }
In your fresh.config.ts
, replace the twindPlugin
import with the tailwind
plugin:
Before:
import twindPlugin from "$fresh/plugins/twindv1.ts"; import twindConfig from "./twind.config.ts"; export default defineConfig({ plugins: [twindPlugin(twindConfig)], });
After:
import tailwind from "$fresh/plugins/tailwind.ts"; export default defineConfig({ plugins: [tailwind()], });
Updating the VSCode Environment
.vscode/extensions.json
Changes: Replace"sastan.twind-intellisense"
with"bradlc.vscode-tailwindcss"
to reflect the new setup.Addition of
.vscode/tailwind.json
: Create a new file namedtailwind.json
in the.vscode
directory with default settings or specific configurations for your project.
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
}
]
},
{
"name": "@responsive",
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
}
]
},
{
"name": "@screen",
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
}
]
},
{
"name": "@variants",
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
}
]
}
]
}
- Updating
settings.json
: Ensure your VSCode settings accommodate the new Tailwind CSS setup by adding configurations that enable proper formatting and IntelliSense.
{
"[typescriptreact]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[javascriptreact]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[javascript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"css.customData": [
".vscode/tailwind.json"
]
}
styles.css
Incorporating Create a styles.css
file that will be processed by the Tailwind plugin. Its default content should include:
@tailwind base;
@tailwind components;
@tailwind utilities;
Remember to import /styles.css
in your project's common header, typically found in the root _app.tsx
.
Ensuring Visibility of All Used Classes
When using dynamic class names, such as text-${color}-600
, ensure that each variant is explicitly mentioned in your source code or included in the safelist
within your tailwind.config.ts
file to avoid purging by Tailwind.
Plugin Configuration and Migration
Migrating
twind.config.ts
totailwind.config.ts
: Begin by convertingtwind
rules to Tailwind plugin syntax. Mosttheme.extend
configurations can be transferred directly without modifications.Adapting to Tailwind's Syntax: Convert any
twind
-specific string rules to Tailwind's@apply
directive within the newstyles.css
.
Removing Grouping Variants
Tailwind CSS does not support twind
's grouping variants syntax (dark:(bg-red-500 text-white)
). These must be expanded to their full form, e.g.,
dark:bg-red-500 dark:text-white
. To identify and replace these instances, I utilized a simple JavaScript script to search through the project files. This
approach can be tailored to fit more complex requirements.
const allElements = document.querySelectorAll("*"); // Select all elements in the DOM
const allClasses = new Set(); // Use a Set to store unique class names
allElements.forEach((el) => {
const classList = el.className;
if (classList && typeof classList === "string") {
const classes = classList.split(/\s+/); // Split class names by whitespace
classes.forEach((className) => {
if (className) allClasses.add(className); // Add each class name to the Set
});
}
});
console.log("Unique classes used in this page:");
console.log(Array.from(allClasses)); // Convert the Set to an Array for easy viewing
// Function to check if a class has styles
function hasStyles(className) {
const tempElement = document.createElement("div");
document.body.appendChild(tempElement);
// Get the computed style for the element
const style1 = JSON.stringify(window.getComputedStyle(tempElement));
tempElement.className = className;
const style2 = JSON.stringify(window.getComputedStyle(tempElement));
// Clean up: remove the temporary element
document.body.removeChild(tempElement);
if (style1 === style2) {
return false;
}
return true;
}
const classesWithoutStyles = [];
// Check each class in allClasses
allClasses.forEach((className) => {
if (!hasStyles(className)) {
classesWithoutStyles.push(className);
}
});
console.log("Classes without styles:");
console.log(classesWithoutStyles);
By running this script on each page, I am able to swiftly identify any overlooked instances that utilize twind's unique syntax. This proactive approach ensures that no such patterns are missed during the transition to Tailwind CSS.
While there's room for enhancement in this script to accommodate more complex scenarios, it has proven to be sufficiently effective for my immediate needs. Its simplicity and efficiency in detecting specific syntax patterns make it a valuable tool in the upgrade process.
Conclusion
Upgrading to Deno Fresh 1.6 and integrating Tailwind CSS requires careful attention to detail but is ultimately rewarding. The process enhances your project's styling capabilities and streamlines development workflows. By following the steps outlined above, you can ensure a smooth transition to this powerful combination of technologies.