@@ -21,7 +21,7 @@ const getRegexMatches = (string, regex) => {
2121 return res ;
2222} ;
2323
24- const getDeclarations = ( cssAst ) =>
24+ const getLightThemeDeclarations = ( cssAst ) =>
2525 cssAst . stylesheet . rules
2626 . filter (
2727 ( node ) =>
@@ -32,6 +32,17 @@ const getDeclarations = (cssAst) =>
3232 . map ( ( node ) => node . declarations . filter ( ( decl ) => decl . type === 'declaration' ) )
3333 . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ; // flatten
3434
35+ const getDarkThemeDeclarations = ( cssAst ) =>
36+ cssAst . stylesheet . rules
37+ . filter (
38+ ( node ) =>
39+ node . type === 'rule' &&
40+ node . selectors &&
41+ node . selectors . some ( ( item ) => item . includes ( `:where(.pf-${ version } -theme-dark)` ) )
42+ )
43+ . map ( ( node ) => node . declarations . filter ( ( decl ) => decl . type === 'declaration' ) )
44+ . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ; // flatten
45+
3546const formatFilePathToName = ( filePath ) => {
3647 // const filePathArr = filePath.split('/');
3748 let prefix = '' ;
@@ -49,7 +60,7 @@ const getLocalVarsMap = (cssFiles) => {
4960 cssFiles . forEach ( ( filePath ) => {
5061 const cssAst = parse ( readFileSync ( filePath , 'utf8' ) ) ;
5162
52- getDeclarations ( cssAst ) . forEach ( ( { property, value, parent } ) => {
63+ getLightThemeDeclarations ( cssAst ) . forEach ( ( { property, value, parent } ) => {
5364 if ( res [ property ] ) {
5465 // Accounts for multiple delcarations out of root scope.
5566 // TODO: revamp CSS var mapping
@@ -72,6 +83,25 @@ const getLocalVarsMap = (cssFiles) => {
7283 return res ;
7384} ;
7485
86+ const getDarkLocalVarsMap = ( cssFiles ) => {
87+ const res = { } ;
88+
89+ cssFiles . forEach ( ( filePath ) => {
90+ const cssAst = parse ( readFileSync ( filePath , 'utf8' ) ) ;
91+
92+ getDarkThemeDeclarations ( cssAst ) . forEach ( ( { property, value, parent } ) => {
93+ if ( property . startsWith ( `--pf-${ version } ` ) || property . startsWith ( '--pf-t' ) ) {
94+ res [ property ] = {
95+ ...res [ property ] ,
96+ [ parent . selectors [ 0 ] ] : value
97+ } ;
98+ }
99+ } ) ;
100+ } ) ;
101+
102+ return res ;
103+ } ;
104+
75105/**
76106 * Generates tokens from CSS in node_modules/@patternfly/patternfly/**
77107 *
@@ -113,23 +143,48 @@ export function generateTokens() {
113143 const cssGlobalVariablesAst = parse (
114144 readFileSync ( require . resolve ( '@patternfly/patternfly/base/patternfly-variables.css' ) , 'utf8' )
115145 ) ;
146+
147+ // Filter light theme variables (exclude dark theme)
116148 cssGlobalVariablesAst . stylesheet . rules = cssGlobalVariablesAst . stylesheet . rules . filter (
117149 ( node ) => ! node . selectors || ! node . selectors . some ( ( item ) => item . includes ( `.pf-${ version } -theme-dark` ) )
118150 ) ;
119151
120- const cssGlobalVariablesMap = getRegexMatches ( stringify ( cssGlobalVariablesAst ) , / ( - - p f - [ \w - ] * ) : \s * ( [ \w - _ ] + ) ; / g) ;
152+ const cssGlobalVariablesMap = {
153+ ...getRegexMatches ( stringify ( cssGlobalVariablesAst ) , / ( - - p f - v 6 - [ \w - ] * ) : \s * ( [ \w - _ ( ) . ] + ) ; / g) ,
154+ ...getRegexMatches ( stringify ( cssGlobalVariablesAst ) , / ( - - p f - t - - [ \w - ] * ) : \s * ( [ ^ ; ] + ) ; / g)
155+ } ;
156+
157+ // Get dark theme variables map
158+ const cssGlobalVariablesDarkMap = { } ;
159+ getDarkThemeDeclarations ( cssGlobalVariablesAst ) . forEach ( ( { property, value } ) => {
160+ if ( property . startsWith ( '--pf' ) ) {
161+ cssGlobalVariablesDarkMap [ property ] = value ;
162+ }
163+ } ) ;
121164
122- const getComputedCSSVarValue = ( value , selector , varMap ) =>
165+ const getComputedCSSVarValue = ( value , selector , varMap , isDark = false ) =>
123166 value . replace ( / v a r \( ( [ \w - ] * ) ( , .* ) ? \) / g, ( full , m1 , m2 ) => {
124167 if ( m1 . startsWith ( `--pf-${ version } -global` ) ) {
125168 if ( varMap [ m1 ] ) {
126169 return varMap [ m1 ] + ( m2 || '' ) ;
127170 } else {
128171 return full ;
129172 }
173+ } else if ( m1 . startsWith ( '--pf-t' ) ) {
174+ // For semantic tokens, check if they exist in the map
175+ if ( varMap [ m1 ] ) {
176+ return varMap [ m1 ] + ( m2 || '' ) ;
177+ } else {
178+ // If not found in global map, try to resolve from local variables (e.g., chart tokens)
179+ if ( selector ) {
180+ return getFromLocalVarsMap ( m1 , selector , isDark ) + ( m2 || '' ) ;
181+ } else {
182+ return full ;
183+ }
184+ }
130185 } else {
131186 if ( selector ) {
132- return getFromLocalVarsMap ( m1 , selector ) + ( m2 || '' ) ;
187+ return getFromLocalVarsMap ( m1 , selector , isDark ) + ( m2 || '' ) ;
133188 }
134189 }
135190 } ) ;
@@ -143,19 +198,20 @@ export function generateTokens() {
143198 }
144199 } ) ;
145200
146- const getVarsMap = ( value , selector ) => {
201+ const getVarsMap = ( value , selector , isDark = false ) => {
147202 // evaluate the value and follow the variable chain
148203 const varsMap = [ value ] ;
204+ const varMapToUse = isDark ? { ...cssGlobalVariablesMap , ...cssGlobalVariablesDarkMap } : cssGlobalVariablesMap ;
149205
150206 let computedValue = value ;
151207 let finalValue = value ;
152208 while ( finalValue . includes ( 'var(--pf' ) || computedValue . includes ( 'var(--pf' ) || computedValue . includes ( '$pf-' ) ) {
153209 // keep following the variable chain until we get to a value
154210 if ( finalValue . includes ( 'var(--pf' ) ) {
155- finalValue = getComputedCSSVarValue ( finalValue , selector , cssGlobalVariablesMap ) ;
211+ finalValue = getComputedCSSVarValue ( finalValue , selector , varMapToUse , isDark ) ;
156212 }
157213 if ( computedValue . includes ( 'var(--pf' ) ) {
158- computedValue = getComputedCSSVarValue ( computedValue , selector ) ;
214+ computedValue = getComputedCSSVarValue ( computedValue , selector , varMapToUse , isDark ) ;
159215 } else {
160216 computedValue = getComputedScssVarValue ( computedValue ) ;
161217 }
@@ -182,21 +238,23 @@ export function generateTokens() {
182238 // then we need to find:
183239 // --pf-${version}-c-chip-group--c-chip--MarginBottom: var(--pf-${version}-global--spacer--xs);
184240 const localVarsMap = getLocalVarsMap ( cssFiles ) ;
241+ const darkLocalVarsMap = getDarkLocalVarsMap ( cssFiles ) ;
185242
186- const getFromLocalVarsMap = ( match , selector ) => {
187- if ( localVarsMap [ match ] ) {
243+ const getFromLocalVarsMap = ( match , selector , isDark = false ) => {
244+ const varsMap = isDark ? { ...localVarsMap , ...darkLocalVarsMap } : localVarsMap ;
245+ if ( varsMap [ match ] ) {
188246 // have exact selectors match
189- if ( localVarsMap [ match ] [ selector ] ) {
190- return localVarsMap [ match ] [ selector ] ;
191- } else if ( Object . keys ( localVarsMap [ match ] ) . length === 1 ) {
247+ if ( varsMap [ match ] [ selector ] ) {
248+ return varsMap [ match ] [ selector ] ;
249+ } else if ( Object . keys ( varsMap [ match ] ) . length === 1 ) {
192250 // only one match, return its value
193- return Object . values ( localVarsMap [ match ] ) [ 0 ] ;
251+ return Object . values ( varsMap [ match ] ) [ 0 ] ;
194252 } else {
195253 // find the nearest parent selector and return its value
196254 let bestMatch = '' ;
197255 let bestValue = '' ;
198- for ( const key in localVarsMap [ match ] ) {
199- if ( localVarsMap [ match ] . hasOwnProperty ( key ) ) {
256+ for ( const key in varsMap [ match ] ) {
257+ if ( varsMap [ match ] . hasOwnProperty ( key ) ) {
200258 // remove trailing * from key to compare
201259 let sanitizedKey = key . replace ( / \* $ / , '' ) . trim ( ) ;
202260 sanitizedKey = sanitizedKey . replace ( / > $ / , '' ) . trim ( ) ;
@@ -206,7 +264,7 @@ export function generateTokens() {
206264 if ( sanitizedKey . length > bestMatch . length ) {
207265 // longest matching key is the winner
208266 bestMatch = key ;
209- bestValue = localVarsMap [ match ] [ key ] ;
267+ bestValue = varsMap [ match ] [ key ] ;
210268 }
211269 }
212270 }
@@ -228,8 +286,10 @@ export function generateTokens() {
228286 const cssAst = parse ( readFileSync ( filePath , 'utf8' ) ) ;
229287 // key is the formatted file name, e.g. c_about_modal_box
230288 const key = formatFilePathToName ( filePath ) ;
289+ // darkDeclarations are the dark theme properties within this file
290+ const darkDeclarations = getDarkThemeDeclarations ( cssAst ) ;
231291
232- getDeclarations ( cssAst )
292+ getLightThemeDeclarations ( cssAst )
233293 . filter ( ( { property } ) => property . startsWith ( '--pf' ) )
234294 . forEach ( ( { property, value, parent } ) => {
235295 const selector = parent . selectors [ 0 ] ;
@@ -243,6 +303,21 @@ export function generateTokens() {
243303 propertyObj . values = varsMap ;
244304 }
245305
306+ // Check if there's a dark theme override for this property
307+ const darkDecl = darkDeclarations . find ( ( decl ) => decl . property === property ) ;
308+ if ( darkDecl ) {
309+ try {
310+ const darkVarsMap = getVarsMap ( darkDecl . value , selector , true ) ;
311+ propertyObj . darkValue = darkVarsMap [ darkVarsMap . length - 1 ] ;
312+ if ( darkVarsMap . length > 1 ) {
313+ propertyObj . darkValues = darkVarsMap ;
314+ }
315+ } catch ( e ) {
316+ // Skip dark value if it can't be resolved
317+ // This can happen when dark theme uses variables that don't exist in the light theme
318+ }
319+ }
320+
246321 fileTokens [ key ] = fileTokens [ key ] || { } ;
247322 fileTokens [ key ] [ selector ] = fileTokens [ key ] [ selector ] || { } ;
248323 fileTokens [ key ] [ selector ] [ formatCustomPropertyName ( property ) ] = propertyObj ;
0 commit comments