Skip to content

Commit

Permalink
Merge pull request #42 from tbela99/color_v4
Browse files Browse the repository at this point in the history
add light-dark() and system colors #41
  • Loading branch information
tbela99 authored Jul 21, 2024
2 parents 23e90df + c8f14fc commit 24798a2
Show file tree
Hide file tree
Showing 60 changed files with 379 additions and 129 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ jobs:
runs-on: ubuntu-latest
steps:
# Your original steps
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install
run: npm install
- name: Test and Coverage
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/jsr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# publish to jsr
name: Publish

on:
push:
branches:
- master

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # The OIDC ID token is used for authentication with JSR.
steps:
- uses: actions/checkout@v4
- run: npx jsr publish
6 changes: 3 additions & 3 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [ 18.x, 20.x, 22.x ]
node-version: [ 18.x, 20.x ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install NPM dependencies
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# v0.6.0
- [x] light-dark() color
- [x] system color

## V0.5.4

- [x] incorrectly expand css nesting rules
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ $ npm install @tbela99/css-parser
- fault-tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
- fast and efficient minification without unsafe transforms, see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html)
- minify colors.
- support css color level 4 & 5: color(), lab(), lch(), oklab(), oklch(), color-mix() and relative color
- support css color level 4 & 5: color(), lab(), lch(), oklab(), oklch(), color-mix(), light-dark(), system colors and relative color
- generate nested css rules
- convert nested css rules to legacy syntax
- generate sourcemap
Expand Down Expand Up @@ -76,7 +76,7 @@ import {transform} from '@tbela99/css-parser/web';

Javascript module from cdn

```javascript
```html

<script type="module">
Expand Down
47 changes: 41 additions & 6 deletions dist/index-umd-web.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@
const D50 = [0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585];
const k = Math.pow(29, 3) / Math.pow(3, 3);
const e = Math.pow(6, 3) / Math.pow(29, 3);
// color module v4
const systemColors = new Set(['ActiveText', 'ButtonBorder', 'ButtonFace', 'ButtonText', 'Canvas', 'CanvasText', 'Field', 'FieldText', 'GrayText', 'Highlight', 'HighlightText', 'LinkText', 'Mark', 'MarkText', 'VisitedText'].map(m => m.toLowerCase()));
// deprecated
const deprecatedSystemColors = new Set(['ActiveBorder', 'ActiveCaption', 'AppWorkspace', 'Background', 'ButtonFace', 'ButtonHighlight', 'ButtonShadow', 'ButtonText', 'CaptionText', 'GrayText', 'Highlight', 'HighlightText', 'InactiveBorder', 'InactiveCaption', 'InactiveCaptionText', 'InfoBackground', 'InfoText', 'Menu', 'MenuText', 'Scrollbar', 'ThreeDDarkShadow', 'ThreeDFace', 'ThreeDHighlight', 'ThreeDLightShadow', 'ThreeDShadow', 'Window', 'WindowFrame', 'WindowText'].map(t => t.toLowerCase()));
// name to color
const COLORS_NAMES = Object.seal({
'aliceblue': '#f0f8ff',
Expand Down Expand Up @@ -2958,7 +2962,7 @@
}
}

const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk', 'color-mix', 'color', 'oklab', 'lab', 'oklch', 'lch'];
const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk', 'color-mix', 'color', 'oklab', 'lab', 'oklch', 'lch', 'light-dark'];
function reduceNumber(val) {
val = String(+val);
if (val === '0') {
Expand Down Expand Up @@ -3226,6 +3230,9 @@
case exports.EnumToken.Div:
return '/';
case exports.EnumToken.ColorTokenType:
if (token.kin == 'light-dark') {
return token.val + '(' + token.chi.reduce((acc, curr) => acc + renderToken(curr, options, cache), '') + ')';
}
if (options.convertColor) {
if (token.cal == 'mix' && token.val == 'color-mix') {
const children = token.chi.reduce((acc, t) => {
Expand Down Expand Up @@ -3319,7 +3326,7 @@
return reduceHexValue(value);
}
}
if (token.kin == 'hex' || token.kin == 'lit') {
if (['hex', 'lit', 'sys', 'dpsys'].includes(token.kin)) {
return token.val;
}
if (Array.isArray(token.chi)) {
Expand Down Expand Up @@ -3585,6 +3592,15 @@
}
let isLegacySyntax = false;
if (token.typ == exports.EnumToken.FunctionTokenType && token.chi.length > 0 && colorsFunc.includes(token.val)) {
if (token.val == 'light-dark') {
const children = token.chi.filter((t) => [exports.EnumToken.IdenTokenType, exports.EnumToken.NumberTokenType, exports.EnumToken.LiteralTokenType, exports.EnumToken.ColorTokenType, exports.EnumToken.FunctionTokenType, exports.EnumToken.PercentageTokenType].includes(t.typ));
if (children.length != 2) {
return false;
}
if (isColor(children[0]) && isColor(children[1])) {
return true;
}
}
if (token.val == 'color') {
const children = token.chi.filter((t) => [exports.EnumToken.IdenTokenType, exports.EnumToken.NumberTokenType, exports.EnumToken.LiteralTokenType, exports.EnumToken.ColorTokenType, exports.EnumToken.FunctionTokenType, exports.EnumToken.PercentageTokenType].includes(t.typ));
const isRelative = children[0].typ == exports.EnumToken.IdenTokenType && children[0].val == 'from';
Expand Down Expand Up @@ -6574,9 +6590,9 @@
}));
}
function getTokenType(val, hint) {
if (val === '' && hint == null) {
throw new Error('empty string?');
}
// if (val === '' && hint == null) {
// throw new Error('empty string?');
// }
if (hint != null) {
return enumTokenHints.has(hint) ? { typ: hint } : { typ: hint, val };
}
Expand Down Expand Up @@ -6705,6 +6721,20 @@
};
}
if (isIdent(val)) {
if (systemColors.has(val.toLowerCase())) {
return {
typ: exports.EnumToken.ColorTokenType,
val,
kin: 'sys'
};
}
if (deprecatedSystemColors.has(val.toLowerCase())) {
return {
typ: exports.EnumToken.ColorTokenType,
val,
kin: 'dpsys'
};
}
return {
typ: val.startsWith('--') ? exports.EnumToken.DashedIdenTokenType : exports.EnumToken.IdenTokenType,
val
Expand Down Expand Up @@ -6986,7 +7016,11 @@
// t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
}
}
t.chi = t.chi.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommaTokenType, exports.EnumToken.CommentTokenType].includes(t.typ));
const filter = [exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType];
if (t.val != 'light-dark') {
filter.push(exports.EnumToken.CommaTokenType);
}
t.chi = t.chi.filter((t) => !filter.includes(t.typ));
continue;
}
if (t.typ == exports.EnumToken.UrlFunctionTokenType) {
Expand Down Expand Up @@ -9491,6 +9525,7 @@
const path = resolve(url, currentFile).absolute;
t = new URL(path, self.origin);
}
// @ts-ignore
return fetch(t, t.origin != self.origin ? { mode: 'cors' } : {}).then(parseResponse);
}

Expand Down
47 changes: 41 additions & 6 deletions dist/index.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

var process = require('node:process');
var promises = require('node:fs/promises');

exports.EnumToken = void 0;
Expand Down Expand Up @@ -182,6 +183,10 @@ const colorFuncColorSpace = ['srgb', 'srgb-linear', 'display-p3', 'prophoto-rgb'
const D50 = [0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585];
const k = Math.pow(29, 3) / Math.pow(3, 3);
const e = Math.pow(6, 3) / Math.pow(29, 3);
// color module v4
const systemColors = new Set(['ActiveText', 'ButtonBorder', 'ButtonFace', 'ButtonText', 'Canvas', 'CanvasText', 'Field', 'FieldText', 'GrayText', 'Highlight', 'HighlightText', 'LinkText', 'Mark', 'MarkText', 'VisitedText'].map(m => m.toLowerCase()));
// deprecated
const deprecatedSystemColors = new Set(['ActiveBorder', 'ActiveCaption', 'AppWorkspace', 'Background', 'ButtonFace', 'ButtonHighlight', 'ButtonShadow', 'ButtonText', 'CaptionText', 'GrayText', 'Highlight', 'HighlightText', 'InactiveBorder', 'InactiveCaption', 'InactiveCaptionText', 'InfoBackground', 'InfoText', 'Menu', 'MenuText', 'Scrollbar', 'ThreeDDarkShadow', 'ThreeDFace', 'ThreeDHighlight', 'ThreeDLightShadow', 'ThreeDShadow', 'Window', 'WindowFrame', 'WindowText'].map(t => t.toLowerCase()));
// name to color
const COLORS_NAMES = Object.seal({
'aliceblue': '#f0f8ff',
Expand Down Expand Up @@ -2956,7 +2961,7 @@ class SourceMap {
}
}

const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk', 'color-mix', 'color', 'oklab', 'lab', 'oklch', 'lch'];
const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk', 'color-mix', 'color', 'oklab', 'lab', 'oklch', 'lch', 'light-dark'];
function reduceNumber(val) {
val = String(+val);
if (val === '0') {
Expand Down Expand Up @@ -3224,6 +3229,9 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
case exports.EnumToken.Div:
return '/';
case exports.EnumToken.ColorTokenType:
if (token.kin == 'light-dark') {
return token.val + '(' + token.chi.reduce((acc, curr) => acc + renderToken(curr, options, cache), '') + ')';
}
if (options.convertColor) {
if (token.cal == 'mix' && token.val == 'color-mix') {
const children = token.chi.reduce((acc, t) => {
Expand Down Expand Up @@ -3317,7 +3325,7 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
return reduceHexValue(value);
}
}
if (token.kin == 'hex' || token.kin == 'lit') {
if (['hex', 'lit', 'sys', 'dpsys'].includes(token.kin)) {
return token.val;
}
if (Array.isArray(token.chi)) {
Expand Down Expand Up @@ -3583,6 +3591,15 @@ function isColor(token) {
}
let isLegacySyntax = false;
if (token.typ == exports.EnumToken.FunctionTokenType && token.chi.length > 0 && colorsFunc.includes(token.val)) {
if (token.val == 'light-dark') {
const children = token.chi.filter((t) => [exports.EnumToken.IdenTokenType, exports.EnumToken.NumberTokenType, exports.EnumToken.LiteralTokenType, exports.EnumToken.ColorTokenType, exports.EnumToken.FunctionTokenType, exports.EnumToken.PercentageTokenType].includes(t.typ));
if (children.length != 2) {
return false;
}
if (isColor(children[0]) && isColor(children[1])) {
return true;
}
}
if (token.val == 'color') {
const children = token.chi.filter((t) => [exports.EnumToken.IdenTokenType, exports.EnumToken.NumberTokenType, exports.EnumToken.LiteralTokenType, exports.EnumToken.ColorTokenType, exports.EnumToken.FunctionTokenType, exports.EnumToken.PercentageTokenType].includes(t.typ));
const isRelative = children[0].typ == exports.EnumToken.IdenTokenType && children[0].val == 'from';
Expand Down Expand Up @@ -6572,9 +6589,9 @@ function parseString(src, options = { location: false }) {
}));
}
function getTokenType(val, hint) {
if (val === '' && hint == null) {
throw new Error('empty string?');
}
// if (val === '' && hint == null) {
// throw new Error('empty string?');
// }
if (hint != null) {
return enumTokenHints.has(hint) ? { typ: hint } : { typ: hint, val };
}
Expand Down Expand Up @@ -6703,6 +6720,20 @@ function getTokenType(val, hint) {
};
}
if (isIdent(val)) {
if (systemColors.has(val.toLowerCase())) {
return {
typ: exports.EnumToken.ColorTokenType,
val,
kin: 'sys'
};
}
if (deprecatedSystemColors.has(val.toLowerCase())) {
return {
typ: exports.EnumToken.ColorTokenType,
val,
kin: 'dpsys'
};
}
return {
typ: val.startsWith('--') ? exports.EnumToken.DashedIdenTokenType : exports.EnumToken.IdenTokenType,
val
Expand Down Expand Up @@ -6984,7 +7015,11 @@ function parseTokens(tokens, options = {}) {
// t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
}
}
t.chi = t.chi.filter((t) => ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommaTokenType, exports.EnumToken.CommentTokenType].includes(t.typ));
const filter = [exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType];
if (t.val != 'light-dark') {
filter.push(exports.EnumToken.CommaTokenType);
}
t.chi = t.chi.filter((t) => !filter.includes(t.typ));
continue;
}
if (t.typ == exports.EnumToken.UrlFunctionTokenType) {
Expand Down
12 changes: 6 additions & 6 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ export declare interface ImportantToken extends BaseToken {
typ: EnumToken.ImportantTokenType;
}

export declare type ColorKind = 'lit' | 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla' | 'hwb' | 'device-cmyk' | 'oklab' | 'oklch' | 'lab' | 'lch' | 'color';
export declare type ColorKind = 'sys' | 'dpsys' | 'lit' | 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla' | 'hwb' | 'device-cmyk' | 'oklab' | 'oklch' | 'lab' | 'lch' | 'color' | 'light-dark';

// export declare type HueInterpolationMethod = 'shorter' | 'longer' | 'increasing' | 'decreasing';

Expand Down Expand Up @@ -820,11 +820,11 @@ export declare interface MinifyFeature {

ordering: number;

register: (options: MinifyOptions | ParserOptions) => void;
run: (ast: AstRule | AstAtRule, options: ParserOptions = {}, parent: AstRule | AstAtRule | AstRuleStyleSheet, context: {
[key: string]: any
}) => void;
cleanup?: (ast: AstRuleStyleSheet, options: ParserOptions = {}, context: { [key: string]: any }) => void;
register(options: MinifyOptions | ParserOptions): void;

// run(ast: AstRule | AstAtRule, options: ParserOptions = {}, parent: AstRule | AstAtRule | AstRuleStyleSheet, context: { [key: string]: any }): void;

// cleanup?(ast: AstRuleStyleSheet, options: ParserOptions = {}, context: { [key: string]: any }): void;
}

export declare interface MinifyFeature {
Expand Down
28 changes: 23 additions & 5 deletions dist/lib/parser/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { walkValues, walk } from '../ast/walk.js';
import { expand } from '../ast/expand.js';
import { parseDeclaration } from './utils/declaration.js';
import { renderToken } from '../renderer/render.js';
import { COLORS_NAMES } from '../renderer/color/utils/constants.js';
import { COLORS_NAMES, systemColors, deprecatedSystemColors } from '../renderer/color/utils/constants.js';
import { tokenize } from './tokenize.js';

const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
Expand Down Expand Up @@ -539,9 +539,9 @@ function parseString(src, options = { location: false }) {
}));
}
function getTokenType(val, hint) {
if (val === '' && hint == null) {
throw new Error('empty string?');
}
// if (val === '' && hint == null) {
// throw new Error('empty string?');
// }
if (hint != null) {
return enumTokenHints.has(hint) ? { typ: hint } : { typ: hint, val };
}
Expand Down Expand Up @@ -670,6 +670,20 @@ function getTokenType(val, hint) {
};
}
if (isIdent(val)) {
if (systemColors.has(val.toLowerCase())) {
return {
typ: EnumToken.ColorTokenType,
val,
kin: 'sys'
};
}
if (deprecatedSystemColors.has(val.toLowerCase())) {
return {
typ: EnumToken.ColorTokenType,
val,
kin: 'dpsys'
};
}
return {
typ: val.startsWith('--') ? EnumToken.DashedIdenTokenType : EnumToken.IdenTokenType,
val
Expand Down Expand Up @@ -951,7 +965,11 @@ function parseTokens(tokens, options = {}) {
// t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
}
}
t.chi = t.chi.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommaTokenType, EnumToken.CommentTokenType].includes(t.typ));
const filter = [EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType];
if (t.val != 'light-dark') {
filter.push(EnumToken.CommaTokenType);
}
t.chi = t.chi.filter((t) => !filter.includes(t.typ));
continue;
}
if (t.typ == EnumToken.UrlFunctionTokenType) {
Expand Down
9 changes: 9 additions & 0 deletions dist/lib/parser/utils/syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ function isColor(token) {
}
let isLegacySyntax = false;
if (token.typ == EnumToken.FunctionTokenType && token.chi.length > 0 && colorsFunc.includes(token.val)) {
if (token.val == 'light-dark') {
const children = token.chi.filter((t) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.LiteralTokenType, EnumToken.ColorTokenType, EnumToken.FunctionTokenType, EnumToken.PercentageTokenType].includes(t.typ));
if (children.length != 2) {
return false;
}
if (isColor(children[0]) && isColor(children[1])) {
return true;
}
}
if (token.val == 'color') {
const children = token.chi.filter((t) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.LiteralTokenType, EnumToken.ColorTokenType, EnumToken.FunctionTokenType, EnumToken.PercentageTokenType].includes(t.typ));
const isRelative = children[0].typ == EnumToken.IdenTokenType && children[0].val == 'from';
Expand Down
Loading

0 comments on commit 24798a2

Please sign in to comment.