From e0a5b6aa89da54c202094405f0b52c704fcf4956 Mon Sep 17 00:00:00 2001 From: xypnox Date: Tue, 21 Nov 2023 16:03:52 +0530 Subject: [PATCH] Add build check for undeclared css variables --- .gitignore | 2 + README.md | 5 + checkCssVars.cjs | 34 +++++ package.json | 9 +- pnpm-lock.yaml | 30 +++-- postcss.config.cjs | 5 + src/buildChecks/cssJsonExport.ts | 5 + src/components/nav/Navbar.astro | 1 + src/content/blog/dark-modes/index.md | 176 +++++++++++++++++++++++++ src/content/blog/sample-blog/index.mdx | 76 +---------- src/layouts/BaseLayout.astro | 3 - src/lib/dataAstro.ts | 24 ++-- src/pages/blag/posts/[...slug].astro | 7 + src/pages/index.astro | 3 +- src/theme.ts | 7 + tsconfig-dev.json | 18 +++ 16 files changed, 301 insertions(+), 104 deletions(-) create mode 100644 checkCssVars.cjs create mode 100644 postcss.config.cjs create mode 100644 src/buildChecks/cssJsonExport.ts create mode 100644 src/content/blog/dark-modes/index.md create mode 100644 tsconfig-dev.json diff --git a/.gitignore b/.gitignore index 6d4c0aa..296696e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ pnpm-debug.log* # macOS-specific files .DS_Store + +cssVariables.json diff --git a/README.md b/README.md index ef174c7..999b501 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,11 @@ All commands are run from the root of the project, from a terminal: --- +# Checks + +- `css variables` + We have a postcss check that throws warnings when a css variable is encountered that has either not been declared in the style tag or is not present in the global theme variables. + # Dependencies - solid-js diff --git a/checkCssVars.cjs b/checkCssVars.cjs new file mode 100644 index 0000000..757ac16 --- /dev/null +++ b/checkCssVars.cjs @@ -0,0 +1,34 @@ +const postcss = require('postcss'); + +const cssVariables = require('./cssVariables.json'); + +console.log({ cssVariables }) + +const plugin = postcss.plugin('postcss-validate-vars', () => { + return (root, result) => { + // Check for undefined variables + const localCssVariables = []; + + root.walkDecls(decl => { + if (decl.prop.startsWith('--')) { + localCssVariables.push(decl.prop); + } + }); + + root.walkDecls(decl => { + if (decl.value.includes('var(')) { + const matches = decl.value.match(/var\(([^)]+)\)/); + if (matches) { + const variable = matches[1]; + if (!cssVariables.includes(variable) && !localCssVariables.includes(variable)) { + const file = decl.source.input.file || 'unknown'; + const line = decl.source.start.line; + result.warn(`${file} - ${line} : Undefined variable ${variable} used in ${decl.toString()}`); + } + } + } + }); + }; +}); + +module.exports = plugin; diff --git a/package.json b/package.json index b66b3a8..9736d15 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "name": "com", - "type": "module", "version": "0.0.1", "scripts": { - "dev": "astro dev", + "dev": "pnpm run convert && pnpm run astro:dev", "start": "astro dev", - "build": "astro build", + "convert": "tsc -p tsconfig-dev.json && node dist/buildChecks/cssJsonExport.js", + "build": "pnpm run convert && pnpm run astro:build", + "astro:build": "astro build", + "astro:dev": "astro dev", "preview": "astro preview", "astro": "astro" }, @@ -24,6 +26,7 @@ "solid-styled-components": "^0.28.5" }, "devDependencies": { + "@types/node": "^20.9.3", "typescript": "^5.2.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36fe8f4..3594196 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ dependencies: version: 2.0.14(solid-js@1.7.7) astro: specifier: ^3.2.2 - version: 3.2.2 + version: 3.2.2(@types/node@20.9.3) astro-icon: specifier: ^0.8.1 version: 0.8.1 @@ -43,6 +43,9 @@ dependencies: version: 0.28.5(solid-js@1.7.7) devDependencies: + '@types/node': + specifier: ^20.9.3 + version: 20.9.3 typescript: specifier: ^5.2.2 version: 5.2.2 @@ -126,7 +129,7 @@ packages: astro: ^3.1.0 dependencies: '@astrojs/prism': 3.0.0 - astro: 3.2.2 + astro: 3.2.2(@types/node@20.9.3) github-slugger: 2.0.0 import-meta-resolve: 3.0.0 mdast-util-definitions: 6.0.0 @@ -153,7 +156,7 @@ packages: '@astrojs/markdown-remark': 3.2.0(astro@3.2.2) '@mdx-js/mdx': 2.3.0 acorn: 8.10.0 - astro: 3.2.2 + astro: 3.2.2(@types/node@20.9.3) es-module-lexer: 1.3.1 estree-util-visit: 1.2.1 github-slugger: 2.0.0 @@ -1189,6 +1192,11 @@ packages: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} dev: false + /@types/node@20.9.3: + resolution: {integrity: sha512-nk5wXLAXGBKfrhLB0cyHGbSqopS+nz0BUgZkUQqSHSSgdee0kssp1IAqlQOu333bW+gMNs2QREx7iynm19Abxw==} + dependencies: + undici-types: 5.26.5 + /@types/parse5@6.0.3: resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} dev: false @@ -1200,7 +1208,7 @@ packages: /@types/sax@1.2.6: resolution: {integrity: sha512-A1mpYCYu1aHFayy8XKN57ebXeAbh9oQIZ1wXcno6b1ESUAfMBDMx7mf/QGlYwcMRaFryh9YBuH03i/3FlPGDkQ==} dependencies: - '@types/node': 17.0.45 + '@types/node': 20.9.3 dev: false /@types/unist@2.0.8: @@ -1376,7 +1384,7 @@ packages: svgo: 2.8.0 dev: false - /astro@3.2.2: + /astro@3.2.2(@types/node@20.9.3): resolution: {integrity: sha512-vLOOGyD2g1HRkCQAFHmHUrVAbMI5sgUqKignBMtXNantgEq1P17sL2cvH0Yy2ibNN9DHZ/4TknXmwHAig8PJNg==} engines: {node: '>=18.14.1', npm: '>=6.14.0'} hasBin: true @@ -1432,7 +1440,7 @@ packages: undici: 5.25.4 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 4.4.10 + vite: 4.4.10(@types/node@20.9.3) vitefu: 0.2.4(vite@4.4.10) which-pm: 2.1.1 yargs-parser: 21.1.1 @@ -4163,6 +4171,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /undici@5.25.4: resolution: {integrity: sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==} engines: {node: '>=14.0'} @@ -4369,13 +4380,13 @@ packages: merge-anything: 5.1.7 solid-js: 1.7.7 solid-refresh: 0.5.3(solid-js@1.7.7) - vite: 4.4.10 + vite: 4.4.10(@types/node@20.9.3) vitefu: 0.2.4(vite@4.4.10) transitivePeerDependencies: - supports-color dev: false - /vite@4.4.10: + /vite@4.4.10(@types/node@20.9.3): resolution: {integrity: sha512-TzIjiqx9BEXF8yzYdF2NTf1kFFbjMjUSV0LFZ3HyHoI3SGSPLnnFUKiIQtL3gl2AjHvMrprOvQ3amzaHgQlAxw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -4403,6 +4414,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.9.3 esbuild: 0.18.20 postcss: 8.4.31 rollup: 3.29.4 @@ -4418,7 +4430,7 @@ packages: vite: optional: true dependencies: - vite: 4.4.10 + vite: 4.4.10(@types/node@20.9.3) dev: false /volar-service-css@0.0.13(@volar/language-service@1.10.1): diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 0000000..ce0f6a3 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('./checkCssVars.cjs') + ], +}; diff --git a/src/buildChecks/cssJsonExport.ts b/src/buildChecks/cssJsonExport.ts new file mode 100644 index 0000000..5bd2ab5 --- /dev/null +++ b/src/buildChecks/cssJsonExport.ts @@ -0,0 +1,5 @@ +import { themeCssVars } from '../theme' + +import { writeFileSync } from 'fs'; + +writeFileSync('./cssVariables.json', JSON.stringify(Object.keys(themeCssVars).map(k => `--${k}`))); diff --git a/src/components/nav/Navbar.astro b/src/components/nav/Navbar.astro index d1bc753..c4e2f1b 100644 --- a/src/components/nav/Navbar.astro +++ b/src/components/nav/Navbar.astro @@ -134,4 +134,5 @@ import '../../scripts/menu' font-size: var(--font-size-lg); box-shadow: 0px 8px 16px #00000080; } + } diff --git a/src/content/blog/dark-modes/index.md b/src/content/blog/dark-modes/index.md new file mode 100644 index 0000000..f1652b9 --- /dev/null +++ b/src/content/blog/dark-modes/index.md @@ -0,0 +1,176 @@ +--- +title: "Dark Modes" +description: "A discussion about the dark modes" +date: 2023-11-22 + +# coverImage: "./test-social3.webp" +# coverAlt: "Some text for describing the cover image" +categories: ["sample"] +tags: ["design", "dev"] + +hidden: true +--- + +The difficulties faced in making a dark mode. + +I have a multitude of thoughts about theming, styling, modes etc, this post is about the difficulties usually encountered in implementing a dark mode. + +# Is it needed? + +Dark mode is cool and all but does it make sense in your situation? + +The point of having modes and the ability to switch between them is directly dependent on the content that is being presented. Not everything needs to be switchable, between light and the dark. + +Whether one should have a dark mode depends on what one wants to show, i.e. it depends on the content being shown. + +The best place for a dark mode are commonly used applications that serve a variety of people, who may prefer one color scheme over the other. One such group is the developers. + +Applications are opened more frequently and people spend more time with their interface than normal websites. Moreover, the usability trumps any branding specific design implementations. Websites, specifically websites of companies, brands etc are more targeted towards conveying a distinct and unique identity, where uniqueness can sometimes trump usability (in the sense of not following the standard layouts). + +If you are a company/startup targeting a developer userbase or similar people that might prefer the dark mode, think whether it is necessary to have a light mode. How about just having the dark mode? + +Moreover, there are now extensions that will convert a well designed light mode to a dark mode. And the users that prefer dark mode would be usually using such extensions. + +--- + +The reasons to implement a dark mode can be: + +- The interface is complex or has to be viewed frequently (ex: Editor, Reader, Dashboard etc.) +- The people using the interface spend a long time on it and may depend on it for their work. +- ~~There is not much to do and there is a lot of free time at hand~~ + +--- + +# The culprits + +Once you have reached the decision that a dark mode is indeed what you want, you can take a step back and think about what you are converting dark mode into. That is, the content. + +One of the most common methods of converting a light mode to a dark mode is to simply switch the background and text colors. While this serves for basic text only content, there are various elements that don't directly translate into a usable dark mode. + +## The code block + +What color mode should the code block be in? + +Should light mode and dark mode both have different color schemes? What if your light mode already has a dark code highlighting? Does this mean we add a light code highlighting theme for dark mode? + +## Contrasty designer + +There are designs for software (websites, products) that use different modes (dark/light) in the same interface to distinguish a sidebar/navigation/sections of a page. + +Ex: Sidebar with black back and white text beside a product dashboard, a website with dark and light sections in the landing page. + +While this is effective to some degree. It is neither dark nor light. And the contrast is needless in most cases. Why would you use a different mode when a border or a slightly different shade of background could have achieved the same? Is it because of a "brand identity"? But even then, what kind of brand is both dark and light and that at the same time? + +## Shady Shadows + +Shadows become extremely hard to see in dark modes. More so if the background is completely black. + +## Only prefers color scheme + +1. Perhaps if you have managed to make the dark theme, tying it only to the prefers color scheme option feels kinda bad. Having the ability is cool, but the inability for the user to select the theme they want instead of forcing them to switch the theme for the entire system is kinda bad. + + +## The ugly gray + +Yes I call all dark mode themes that are monochrome and use a grey background ugly. And I will die on this hill. Especially the ones of the old material design (pre v3) days. + +https://m2.material.io/design/color/dark-theme.html#properties + +https://m3.material.io/styles/color/choosing-a-scheme + +And I think these gray themes are what have given the dark mode the aura of unsaturated monochromatic soulless interfaces. As if colors are meant to be reserved for the light mode. + +--- + +# Some good things + +## Colors that were lost + +Some colors look bad on white background if used as text. Yellow is a prime example. But in the world of dark mode, yellow literally shines! + +// Add example yellow calry website + +## CSS variables for theming + +When I dwelled in the world of react and styled components, I used their theme providers which functioned as: + +```tsx +const Button = styled.button` + /* Color the border and text with theme.main */ + color: ${props => props.theme.main}; + border: 2px solid ${props => props.theme.main}; +`; + +// Define what props.theme will look like +const theme = { + main: "mediumseagreen" +}; + +component( +
+ + + +
+); +``` + +But when the themes changed, all the css was recalculated and generated again, which was suboptimal. Moreover this strategy stops working once you move out of the react ecosystem. The better way of theming is to use css variables. + +We define the variables ones for each theme: + +```css +:root { + --primary: mediumseagreen; +} +.dark { + --primary: skyblue; +} +``` + +And then we use those variables in the css styles: + +```css +button { + color: var(--primary); +} +``` + +We can add simple code to add a .dark class to our body/html element to toggle between the themes. None of the styles are recomputed, and this can be used across frameworks or even without any framework. Moreover you can always override the theme variables in smaller contexts: + +```html +
+ +
+ + +``` + +## A step further via objects + +But these do not have type safety. When you write css and later refactor any of the theme variables, the older ones are left dangling. While fallbacks are nice, refactoring still requires manually scouring for the older ones are rewriting them again. Moreover these errors can not be found in the build step and will easily mess up your stylesheets. + +I have found a promising solution. Which has type safety when using css-in-js. But the basic structure remains same. + +The way I organize the themes is to declare the variables as a js object. + +```ts +const theme = { +} +``` + +--- + +# Resources + + +- https://tonsky.me/ + Cool dark mode with round light where the mouse is. + Don't know about accessibility but emulates the torchlight under a quilt nicely. + + + diff --git a/src/content/blog/sample-blog/index.mdx b/src/content/blog/sample-blog/index.mdx index 5dbb2f9..9a7e14a 100644 --- a/src/content/blog/sample-blog/index.mdx +++ b/src/content/blog/sample-blog/index.mdx @@ -13,7 +13,7 @@ hidden: true import BlogPostCard from '../../../components/BlogPostCard.astro'; - +This is a sample blog posts to test and tweak the components and elements present in the blog posts. Please enjoy/ignore accordingly. # This is a sample blog @@ -108,13 +108,6 @@ Start numbering with offset: Inline `code` -Indented code - - // Some comments - line 1 of code - line 2 of code - line 3 of code - Block code "fences" ``` @@ -168,73 +161,6 @@ With a reference later in the document defining the URL location: [id]: https://octodex.github.com/images/dojocat.jpg 'The Dojocat' -## Plugins - -The killer feature of `markdown-it` is very effective support of -[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). - -### [Emojies](https://github.com/markdown-it/markdown-it-emoji) - -> Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: -> -> Shortcuts (emoticons): :-) :-( 8-) ;) - -see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. - -### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) - -- 19^th^ -- H~2~O - -### [mark](https://github.com/markdown-it/markdown-it-mark) - -==Marked text== - -### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) - -Footnote 1 link[^first]. - -Footnote 2 link[^second]. - -Inline footnote^[Text of inline footnote] definition. - -Duplicated footnote reference[^second]. - -[^first]: Footnote **can have markup** - - and multiple paragraphs. - -[^second]: Footnote text. - -### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) - -Term 1 - -: Definition 1 -with lazy continuation. - -Term 2 with _inline markup_ - -: Definition 2 -`{ some code, part of Definition 2 }` - -_Compact style:_ - -Term 1 -~ Definition 1 - -Term 2 -~ Definition 2a -~ Definition 2b - -### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) - -This is HTML abbreviation example. - -It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. - -\*[HTML]: Hyper Text Markup Language - --- # Title diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 6e7ba3e..5cabfd6 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -59,9 +59,6 @@ const { title, showLoading } = Astro.props;