Devzstudio / tailwind_to_css

Convert tailwind class to css
https://tailwind-to-css.vercel.app/
MIT License
447 stars 51 forks source link

Use Tailwind to extract actual CSS rules (instead of interpreting TW classes with a cheatsheet) #47

Open ADTC opened 4 weeks ago

ADTC commented 4 weeks ago

Update: Happy to announce we have a fully working Tailwind-to-CSS converter that's server-based and doesn't depend on cheatsheets here: https://tailwind-to-css-three.vercel.app/

Source code is here: https://github.com/ADTC/tailwind_to_css


Previous Update: I made a version that does this: https://tailwind-to-css-three.vercel.app/


I think the best way to get the CSS would be to actually apply the Tailwind classes to a <div> element, and then extract the rules like this:

// Function to get applied CSS rules
function getAppliedCSSRules(element) {
  const stylesheets = Array.from(document.styleSheets);
  const appliedRules = [];

  stylesheets.forEach(sheet => {
    try {
      const rules = Array.from(sheet.cssRules);

      rules.forEach(rule => {
        if (rule.type === CSSRule.STYLE_RULE && element.matches(rule.selectorText)) {
          let css = rule.cssText;
          if (!css.startsWith('.')) return;
          css = css.substring(css.indexOf('{') + 1, css.lastIndexOf('}')).trim();
          css = css.replace(/; /g, ';\n');
          appliedRules.push(css);
        }
      });
    } catch (e) {
      console.warn('Access to stylesheet is restricted:', sheet.href);
    }
  });

  return appliedRules;
}

// Create the element
const element = document.createElement('DIV');
document.body.appendChild(element);

// Apply the Tailwind classes
element.className = input;

// Get applied CSS rules
const appliedRules = getAppliedCSSRules(element);
const concatenatedRules = appliedRules.join('\n');

// Remove the element
element.remove();
console.log(concatenatedRules); // or return the string

It will be far superior because you no longer need to have an up-to-date cheat sheet, and it will work with crazy TW classes like the ones in #45.

ADTC commented 4 weeks ago

I made a version that does this: https://tailwind-to-css-three.vercel.app/

It has completely eliminated the need for the cheatsheet by simply using Tailwind itself to generate the CSS. It seems to work well so far, but may need further testing.

Source code: https://github.com/ADTC/tailwind_to_css (Willing to open PR if this repo's owner is interested.)

ADTC commented 4 weeks ago

TODO: Switch to server-side processing, which will help cover more situations like dynamic classes described in #45 which is currently still not possible.

Revert changes to tailwind config, and do this:

// pages/api/generate-css.js
import postcss from 'postcss';
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';

export default async function handler(req, res) {
  const { classes } = req.body;

  const css = `
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    .generated {
      @apply ${classes};
    }
  `;

  try {
    const result = await postcss([tailwindcss, autoprefixer])
      .process(css, { from: undefined })
      .then(result => result.css);

    res.status(200).send(result);
  } catch (error) {
    res.status(500).send(error.toString());
  }
}

Inject the CSS from the API into document before generating class list. (Or somehow optimize the process to directly use the CSS from the API.)

Example code:

import { useState } from 'react';

export default function Home() {
  const [classes, setClasses] = useState('');
  const [error, setError] = useState(null);

  const handleGenerateCSS = async () => {
    try {
      const response = await fetch('/api/generate-css', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ classes }),
      });

      if (!response.ok) throw new Error('Failed to generate CSS');

      const css = await response.text();
      const style = document.createElement('style');
      style.textContent = css;
      document.head.appendChild(style);
    } catch (err) {
      setError(err.message);
    }
  };

  return (
    <div className="container">
      <h1>Dynamic Tailwind CSS Generator</h1>
      <input
        type="text"
        value={classes}
        onChange={e => setClasses(e.target.value)}
        placeholder="Enter Tailwind CSS classes"
      />
      <button onClick={handleGenerateCSS}>Generate CSS</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <div className={`generated ${classes}`}>
        This is a preview with dynamic Tailwind CSS classes.
      </div>
    </div>
  );
}
ADTC commented 4 weeks ago

Update: Happy to announce we have a fully working Tailwind-to-CSS converter that's server-based and doesn't depend on cheatsheets here: https://tailwind-to-css-three.vercel.app/