Opens an external site in a new window
Pray for peace.
RODNEY LAB
  • Home
  • Plus +
  • Projects
  • Giving
  • Contact
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Contact

Deno Fresh PostCSS: Future CSS with Deno 💅 # Deno Fresh PostCSS: Future CSS with Deno 💅 #

Deno Fresh PostCSS
  1. Rodney Lab Home
  2. Rodney Lab Blog Posts
  3. Deno Blog Posts
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Deno Fresh PostCSS: Future CSS with Deno 💅 #

Published: 3 weeks ago
6 minute read Gunning Fog Index: 5.3
Content by Rodney
Author Image: Rodney from Rodney Lab
SHARE:

🛸 Using PostCSS & Future CSS with Deno Fresh #

In this post on Deno Fresh PostCSS, we take a look at setting up some vanilla CSS tooling for Deno Fresh. Deno Fresh is an SSR site builder. It has instant deploys with no build step. The absence of a build step sets Deno Fresh apart from other site builders like Astro or SvelteKit. Both of those process CSS, minify and handle some transformations for you. Although Deno Fresh lacks those features, the upside is that you have more control over your CSS; you make the decisions, not the tooling. That said, you need to do a little setup work to use popular tooling like PostCSS. That is what we look at here.

PostCSS tooling transforms input CSS. You can use it to generate prefixes automatically for legacy browsers or to minify the CSS. Minification just removes white-space and comments from CSS, optimizing the CSS for delivery to a browser. With our setup, we can keep the original CSS file (with comments and white-space). Each time we save it, under the hood, PostCSS will generate the shipped version. We do not just look at minification here. We also add some PostCSS processing to handle modern or future CSS, which is not yet fully integrated into the CSS Specification.

🧱 What are we Building? #

Rather than build a project from scratch, we will just look at the setup we need to add to a Deno Fresh project to use PostCSS. We will see:

  • how you can configure PostCSS itself in Deno Fresh
  • how you can automatically generate processed CSS each time you save a CSS input file
  • a way to cache your static CSS assets with Deno Fresh

If that sounds good, then let’s get started.

⚙️ Deno Import Map #

Let’s start by updating the import_map.json file in the project root directory:

    
1 {
2 "imports": {
3 "@/": "./",
4 "$fresh/": "https://deno.land/x/[email protected]/",
5 "preact": "https://esm.sh/[email protected]",
6 "preact/": "https://esm.sh/[email protected]/",
7 "preact-render-to-string": "https://esm.sh/*[email protected]",
8 "@preact/signals": "https://esm.sh/*@preact/[email protected]",
9 "@preact/signals-core": "https://esm.sh/*@preact/[email protected]",
10 "$std/": "https://deno.land/[email protected]/",
11 "postcss/": "https://deno.land/x/[email protected]/"
12 }
13 }

We added an import path alias in line 3 which we will use later. We also added the Deno Standard Library and a Deno PostCSS package.

💅 Post CSS Config #

The config itself will not be too different to what you are already familiar with from Node-based tooling. The main difference will be that we use an npm: prefix on imports. Create postcss.config.ts in the project root directory with the following content:

postcss.config.ts
typescript
    
import autoprefixer from "npm:autoprefixer";
import csso from "npm:postcss-csso";
import customMediaPlugin from "npm:postcss-custom-media";
import postcssPresetEnv from "npm:postcss-preset-env";
export const config = {
plugins: [
customMediaPlugin(),
postcssPresetEnv({
stage: 3,
features: {
"nesting-rules": true,
"custom-media-queries": true,
"media-query-ranges": true,
},
}),
autoprefixer(),
csso(),
],
};

👀 Watch Script #

The last main piece is the watch script. This watches the CSS input folder for changes and runs PostCSS each time you save a file there. We will use a styles directory in the project root folder as the CSS input directory. When we save a CSS file there, the watch script will output the result to static/styles. static is for content which you want Deno Fresh to serve statically from your deployed project.

Create build-css.ts in the project root directory with the content below:

build-css.ts
typescript
    
1 import { debounce } from "$std/async/mod.ts";
2 import { relative, resolve } from "$std/path/mod.ts";
3 import { config } from "@/postcss.config.ts";
4 import postcss from "postcss/mod.js";
5
6 const STYLES_INPUT_DIRECTORY = "styles";
7
8 async function buildStyles(path: string) {
9 try {
10 const css = await Deno.readTextFile(path);
11
12 const { css: outputCss } = await postcss(config.plugins).process(css, {
13 from: undefined,
14 });
15
16 const __dirname = resolve();
17 const outputPath = `./static/${relative(__dirname, path)}`;
18 console.log(`Updating styles for ${outputPath}`);
19 await Deno.writeTextFile(outputPath, outputCss);
20 } catch (error: unknown) {
21 console.error(`Error building styles for path ${path}: ${error as string}`);
22 }
23 }
24
25 const debouncedBuildStyles = debounce(async (path: string) => {
26 await buildStyles(path);
27 }, 200);
28
29 const stylesOutputDirectory = `static/${STYLES_INPUT_DIRECTORY}`;
30 try {
31 Deno.statSync(stylesOutputDirectory);
32 } catch (error: unknown) {
33 if (error instanceof Deno.errors.NotFound) {
34 Deno.mkdir(stylesOutputDirectory);
35 }
36 }
37
38 const watcher = Deno.watchFs([`./${STYLES_INPUT_DIRECTORY}`]);
39 for await (const event of watcher) {
40 const { paths } = event;
41 paths.forEach((path) => {
42 debouncedBuildStyles(path);
43 });
44 }
  • The buildStyles function (line 8) takes an updated styles path and processes it with PostCSS, saving the output to static/styles.
  • Calling debounceBuildStyles instead of buildStyles directly, stops the CSS building function from firing off too often. We limit it to run once at most in any 200 ms period.
  • Lines 29 – 36 will run once when we start up the build CSS task (more about that in a moment) and create the output static/styles directory if it does not yet exist.
  • Lines 38 – 44 contain the watch code for file changes in the styles directory. If you added new files before firing up the build CSS task, just save them again once the task is running to make sure PostCSS processes them.

💄 Build CSS Task #

Next, we can create a Deno task to run the watch script created in the last section. Update the deno.json file in the project root directory to add a build:css task:

deno.json
json
    
1 {
2 "tasks": {
3 "build:css": "deno run --allow-env=DENO_ENV --allow-read --allow-write build-css.ts",
4 "start": "deno run -A --watch=static/,routes/ dev.ts"
5 },
6 "importMap": "./import_map.json",
7 "compilerOptions": {
8 "jsx": "react-jsx",
9 "jsxImportSource": "preact"
10 }
11 }

We give the task just the permissions it needs here. These are read-write access and access to the DENO_ENV environment variable. You can be more restrictive if you prefer, listing the specific directories  Deno will apply the read and write permissions to.

You can open a new Terminal tab and run this new task there:

    
mkdir styles # create a `styles` directory if you don't yet have one
deno task build:css

Then in your previous Terminal tab, start up the Deno Fresh server (as usual) by running deno task start. Try creating a new CSS file at styles/global.css for example. If you open up static/styles/global.css you should see a minified version of this CSS which PostCSS has generated.

🗳 Poll #

What is your most valued CSS tool?
Voting reveals latest results.

💯 Deno Fresh PostCSS: Testing #

Add a few more CSS files and try using new features like range media queries. You can include the CSS in a rel tag in one of your Preact markup files:

index.tsx
typescript
    
1 import { asset, Head } from "$fresh/runtime.ts";
2
3 export default function Home() {
4 return (
5 <Fragment>
6 <Head>
7 <link rel="stylesheet" href={asset("/styles/fonts.css")} />
8 <link rel="stylesheet" href={asset("/styles/global.css")} />
9 <!-- TRUNCATED... -->
10 </Head>
11 <main className="wrapper">
12 <BannerImage />
13 <h1 className="heading">FRESH</h1>
14 <p className="feature">Fresh 🍋 new framework!</p>
15 <Video />
16 </main>
17 </Fragment>
18 );
19 }

Here I wrapped the href for the stylesheets in the asset function. This is helpful for cache busting. Deno Fresh will hash the content of the files and append the calculated hash to the filename. When the CSS changes, the hash and consequently file name change, so no outdated CSS cached in-browser or in a CDN will be applied.

Take a look in static/styles. Your file should contain minified CSS and look something like this:

    
:root{--colour-dark:hsl(242deg 94% 7%);--colour-light:hsl(260deg 43% 97%);--colour-brand:hsl(50deg 100% 56%);--colour-theme:hsl(204deg 69% 50%);--colour-alt:hsl(100deg 100% 33%);--spacing-0:0;--spacing-1:0.25rem; /* TRUNCATED...*/

🙌🏽 Deno Fresh PostCSS: Wrapping Up #

We had an introduction to how you can set up PostCSS with Deno Fresh. In particular, you saw:

  • Deno code for triggering a function when a file changes
  • how to be selective in Deno task permissions
  • some Deno file APIs

The complete code for this project is in the Rodney Lab GitHub repo . I do hope the post has either helped you with an existing project or provided some inspiration for a new one. As an extension, you can add all your favourite future CSS rules to the PostCSS config. Beyond PostCSS for linting your input CSS, consider trying stylelint .

Get in touch if you have some questions about this content or suggestions for improvements. You can add a comment below if you prefer. Also, reach out with ideas for fresh content on Deno or other topics!

🏁 Deno Fresh PostCSS: Summary #

Can you use vanilla CSS with Deno Fresh? #

Yes! You will get prompted to set up the Tailwind plugin when you spin up a Fresh project with the CLI tool. That said, if you prefer vanilla CSS you can just add your CSS source to the static folder. You can then link the added file using rel tags in your Preact markup. We saw you can even use PostCSS to transform your input CSS. In this case, add your source CSS to another folder and configure PostCSS to output to `static`.

Is there a Deno PostCSS module? #

Yes, there is a PostCSS module on deno.land/x (https://deno.land/x/postcss). You will likely want to add in your favourite PostCSS rules and plugins too. We saw you can import them right from npm. As an example, to use the CSSO plugin for minifying CSS, write an import statement like: `import csso from "npm:postcss-csso";`. I saw no issues using autoprefixer, postcss-csso, postcss-custom-media or postcss-preset-env in Deno.

How can you cache-bust static assets with Deno Fresh? #

Let’s say you have a static global.css file in your project served at `https://example.com/styles/global.css`. For optimal performance, you will probably want browsers and CDNs to cache this file. However, when you update the file at a later date, there is a risk the browser or CDN serves up the stale, cached version instead of your new one. Cache-busting is a technique to avoid this happening. Deno Fresh provides a neat solution here. When you add a link in your markup to a CSS file or other resource (in the `static` folder) update the link. Change href=`/styles/global.css` to `href={asset("/styles/fonts.css")}`. You can import the `asset` function from `$fresh/runtime.ts`. It adds a hash to the linked path in your markup, so a stale cached version never gets served.

🙏🏽 Deno Fresh PostCSS: Feedback #

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, then please consider supporting me through Buy me a Coffee.

@askRodney avatar

Rodney

@askRodney

Just dropped a new post on setting up PostCSS with 🍋Deno Fresh.

Handy for minifying your CSS and also using future CSS today.

Hope you find it useful!

#askRodney #learndeno #futurecss https://t.co/glVJHU7YQt

— Rodney (@askRodney) March 8, 2023

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @[email protected]  on Mastodon and also the #rodney  Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as Deno. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Thanks for reading this post. I hope you found it valuable. Please get in touch with your feedback and suggestions for posts you would like to see. Read more about me …

Rodney from Rodney Lab
TAGS:
DENOCSS

Reposts:

Reposts

  • romainmenke profile avatar
  • PostCSS profile avatar

Likes:

Likes

  • CloudCannon profile avatar
  • Adam Argyle profile avatar
  • romainmenke profile avatar
  • Raul Nohea Goodness profile avatar
Reposts & likes provided by Twitter via Webmentions.

Related Post

Astro Server-Side Rendering: Edge Search Site

Astro Server-Side Rendering: Edge Search Site

plus
astro
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Leave a comment …

Your information will be handled in line with our Privacy Policy .

Ask for more

1 Nov 2022 — Astro Server-Side Rendering: Edge Search Site
3 Oct 2022 — Svelte eCommerce Site: SvelteKit Snipcart Storefront
1 Sept 2022 — Get Started with SvelteKit Headless WordPress

Copyright © 2020 – 2023 Rodney Johnson. All Rights Reserved. Please read important copyright and intellectual property information.

  • Home
  • Plus +
  • Newsletter
  • Contact
  • Terms of Use
  • Privacy Policy
We use cookies  to enhance visitors’ experience. Please click the “Options” button to make your choice.  Learn more here.