From 6ca640387822170fc6ed2d1acd6e66afe201b247 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Mon, 17 Jun 2024 15:09:45 +0200 Subject: [PATCH 1/4] Add support for custom cssStyle and compiledStyles for custom classDefs --- packages/mermaid/src/diagrams/flowchart/flowDb.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index 7fe2e3a73..c65c83677 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -806,7 +806,8 @@ const addNodeFromVertex = ( labelStyle: '', parentId, padding: config.flowchart?.padding || 8, - cssStyles: vertex.styles.join(' '), + cssStyles: vertex.styles, + cssCompiledStyles: getCompiledStyles(vertex.classes), cssClasses: vertex.classes.join(' '), shape: getTypeFromVertex(vertex), dir: vertex.dir, @@ -820,6 +821,16 @@ const addNodeFromVertex = ( } }; +function getCompiledStyles(classDefs: string[]) { + let compiledStyles: string[] = []; + for (const customClass of classDefs) { + if (classes.get(customClass)) { + compiledStyles = [...compiledStyles, ...(classes.get(customClass)?.styles ?? [])]; + } + } + return compiledStyles; +} + export const getData = () => { const config = getConfig(); const nodes: Node[] = []; @@ -844,7 +855,7 @@ export const getData = () => { labelStyle: '', parentId: parentDB.get(subGraph.id), padding: config.flowchart?.padding || 8, - cssStyles: '', + cssStyles: [], cssClasses: '', shape: 'rect', dir: subGraph.dir, From 4a8c24a633476a654fefc92db5aaf813cf451d68 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Mon, 17 Jun 2024 15:19:03 +0200 Subject: [PATCH 2/4] Add support for custom cssStyle and compiledStyles for custom classDefs --- packages/mermaid/src/diagrams/flowchart/flowDb.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index c65c83677..66c852a14 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -824,8 +824,14 @@ const addNodeFromVertex = ( function getCompiledStyles(classDefs: string[]) { let compiledStyles: string[] = []; for (const customClass of classDefs) { - if (classes.get(customClass)) { - compiledStyles = [...compiledStyles, ...(classes.get(customClass)?.styles ?? [])]; + const cssClass = classes.get(customClass); + if (cssClass) { + if (cssClass.styles) { + compiledStyles = [...compiledStyles, ...(cssClass.styles ?? [])]; + } + if (cssClass.textStyles) { + compiledStyles = [...compiledStyles, ...(cssClass.textStyles ?? [])]; + } } } return compiledStyles; From a6f3316ddba5ba58266ac5d373325e5e9c4b550b Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Mon, 17 Jun 2024 15:42:45 +0200 Subject: [PATCH 3/4] #5237 passing cssStyles and cssCompiled style arrays --- .../mermaid/src/diagrams/state/dataFetcher.js | 160 ++++++++++++++---- .../diagrams/state/parser/state-style.spec.js | 41 +++++ .../diagrams/state/parser/stateDiagram.jison | 18 ++ .../mermaid/src/diagrams/state/stateCommon.ts | 2 + .../mermaid/src/diagrams/state/stateDb.js | 22 ++- .../rendering-elements/shapes/drawRect.ts | 13 +- .../shapes/handdrawnStyles.ts | 50 +++++- .../rendering-elements/shapes/util.js | 1 + 8 files changed, 255 insertions(+), 52 deletions(-) diff --git a/packages/mermaid/src/diagrams/state/dataFetcher.js b/packages/mermaid/src/diagrams/state/dataFetcher.js index a94fb681d..789deae70 100644 --- a/packages/mermaid/src/diagrams/state/dataFetcher.js +++ b/packages/mermaid/src/diagrams/state/dataFetcher.js @@ -54,21 +54,39 @@ export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOM return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`; } -const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, look) => { +const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, look, classes) => { // graphItemCount = 0; log.trace('items', doc); doc.forEach((item) => { switch (item.stmt) { case STMT_STATE: - dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look); + dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes); break; case DEFAULT_STATE_TYPE: - dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look); + dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes); break; case STMT_RELATION: { - dataFetcher(parentParsedItem, item.state1, diagramStates, nodes, edges, altFlag, look); - dataFetcher(parentParsedItem, item.state2, diagramStates, nodes, edges, altFlag, look); + dataFetcher( + parentParsedItem, + item.state1, + diagramStates, + nodes, + edges, + altFlag, + look, + classes + ); + dataFetcher( + parentParsedItem, + item.state2, + diagramStates, + nodes, + edges, + altFlag, + look, + classes + ); const edgeData = { id: 'edge' + graphItemCount, start: item.state1.id, @@ -135,7 +153,7 @@ let cssClasses = newClassesList(); // style classes defined by a classDef * @param nodes * @param nodeData */ -function insertOrUpdateNode(nodes, nodeData) { +function insertOrUpdateNode(nodes, nodeData, classes) { if (!nodeData.id || nodeData.id === '' || nodeData.id === '') { return; } @@ -143,29 +161,62 @@ function insertOrUpdateNode(nodes, nodeData) { //Populate node style attributes if nodeData has classes defined if (nodeData.cssClasses) { nodeData.cssClasses.split(' ').forEach((cssClass) => { - if (cssClasses[cssClass]) { - cssClasses[cssClass].styles.forEach((style) => { - // Populate nodeData with style attributes specifically to be used by rough.js - if (style && style.startsWith('fill:')) { - nodeData.backgroundColor = style.replace('fill:', ''); - } - if (style && style.startsWith('stroke:')) { - nodeData.borderColor = style.replace('stroke:', ''); - } - if (style && style.startsWith('stroke-width:')) { - nodeData.borderWidth = style.replace('stroke-width:', ''); - } + if (classes.get(cssClass)) { + const classDef = classes.get(cssClass); + // classDef.styles.forEach((style) => { + // nodeData.cssCompiledStyles += style + ','; + // // Populate nodeData with style attributes specifically to be used by rough.js + // if (style && style.startsWith('fill:')) { + // nodeData.backgroundColor = style.replace('fill:', ''); + // } + // if (style && style.startsWith('stroke:')) { + // nodeData.borderColor = style.replace('stroke:', ''); + // } + // if (style && style.startsWith('stroke-width:')) { + // nodeData.borderWidth = style.replace('stroke-width:', ''); + // } - nodeData.cssStyles += style + ';'; - }); - cssClasses[cssClass].textStyles.forEach((style) => { - nodeData.labelStyle += style + ';'; - if (style && style.startsWith('fill:')) { - nodeData.labelTextColor = style.replace('fill:', ''); - } - }); + // nodeData.cssStyles += style + ';'; + // }); + + // classDef.textStyles.forEach((style) => { + // nodeData.labelStyle += style + ';'; + // if (style && style.startsWith('fill:')) { + // nodeData.labelTextColor = style.replace('fill:', ''); + // } + // }); + nodeData.cssCompiledStyles = [...nodeData.cssCompiledStyles, ...classDef.styles]; } }); + //Populate node style attributes if nodeData has classes defined + if (nodeData.cssStyles) { + // nodeData.cssStyles.split(' ').forEach((csStyle) => { + // if (classes[cssClass]) { + // classes[cssClass].styles.forEach((style) => { + // // Populate nodeData with style attributes specifically to be used by rough.js + // if (style && style.startsWith('fill:')) { + // nodeData.backgroundColor = style.replace('fill:', ''); + // } + // if (style && style.startsWith('stroke:')) { + // nodeData.borderColor = style.replace('stroke:', ''); + // } + // if (style && style.startsWith('stroke-width:')) { + // nodeData.borderWidth = style.replace('stroke-width:', ''); + // } + // nodeData.cssStyles += style + ';'; + // }); + // classes[cssClass].textStyles.forEach((style) => { + // nodeData.labelStyle += style + ';'; + // if (style && style.startsWith('fill:')) { + // nodeData.labelTextColor = style.replace('fill:', ''); + // } + // }); + // } + // }); + } + // nodeData.labelTextColor = '#ffffff'; + // nodeData.labelStyle = 'color:#ffffff'; + // nodeData.cssStyles = 'fill:#f77'; } const existingNodeData = nodes.find((node) => node.id === nodeData.id); if (existingNodeData) { @@ -197,7 +248,7 @@ function getClassesFromDbInfo(dbInfoItem) { } //add comma for all other classes else { - classStr += dbInfoItem.classes[i] + ' '; + classStr += dbInfoItem.classes[i] + ','; } } return classStr; @@ -206,9 +257,41 @@ function getClassesFromDbInfo(dbInfoItem) { } } } -export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, altFlag, look) => { +/** + * Get classes from the db for the info item. + * If there aren't any or if dbInfoItem isn't defined, return an empty string. + * Else create 1 string from the list of classes found + * + * @param {undefined | null | object} dbInfoItem + * @returns {string} + */ +function getStylesFromDbInfo(dbInfoItem) { + if (dbInfoItem === undefined || dbInfoItem === null) { + return; + } else { + if (dbInfoItem.styles) { + return dbInfoItem.styles; + } else { + return []; + } + } +} +export const dataFetcher = ( + parent, + parsedItem, + diagramStates, + nodes, + edges, + altFlag, + look, + classes +) => { const itemId = parsedItem.id; - const classStr = getClassesFromDbInfo(diagramStates.get(itemId)); + const dbState = diagramStates.get(itemId); + const classStr = getClassesFromDbInfo(dbState); + const style = getStylesFromDbInfo(dbState); + + log.info('dataFetcher parsedItem', parsedItem, dbState, style); if (itemId !== 'root') { let shape = SHAPE_STATE; @@ -229,6 +312,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt shape, description: common.sanitizeText(itemId, getConfig()), cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`, + cssStyles: style, }; } @@ -287,7 +371,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt shape: newNode.shape, label: newNode.description, cssClasses: newNode.cssClasses, - cssStyles: '', + cssCompiledStyles: [], + cssStyles: newNode.cssStyles, id: itemId, dir: newNode.dir, domId: stateDomId(itemId, graphItemCount), @@ -319,7 +404,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt label: parsedItem.note.text, cssClasses: CSS_DIAGRAM_NOTE, // useHtmlLabels: false, - cssStyles: '', // styles.style, + cssStyles: [], + cssCompilesStyles: [], id: itemId + NOTE_ID + '-' + graphItemCount, domId: stateDomId(itemId, graphItemCount, NOTE), type: newNode.type, @@ -334,7 +420,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt shape: SHAPE_NOTEGROUP, label: parsedItem.note.text, cssClasses: newNode.cssClasses, - cssStyles: '', // styles.style, + cssStyles: [], id: itemId + PARENT_ID, domId: stateDomId(itemId, graphItemCount, PARENT), type: 'group', @@ -352,11 +438,11 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt nodeData.parentId = parentNodeId; //insert groupData - insertOrUpdateNode(nodes, groupData); + insertOrUpdateNode(nodes, groupData, classes); //insert noteData - insertOrUpdateNode(nodes, noteData); + insertOrUpdateNode(nodes, noteData, classes); //insert nodeData - insertOrUpdateNode(nodes, nodeData); + insertOrUpdateNode(nodes, nodeData, classes); let from = itemId; let to = noteData.id; @@ -382,12 +468,12 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt look, }); } else { - insertOrUpdateNode(nodes, nodeData); + insertOrUpdateNode(nodes, nodeData, classes); } } if (parsedItem.doc) { log.trace('Adding nodes children '); - setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look); + setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look, classes); } }; diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js index 1be9148b8..48a8b7e1b 100644 --- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js +++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js @@ -217,4 +217,45 @@ describe('ClassDefs and classes when parsing a State diagram', () => { }); }); }); + + describe('style statement for a state (style)', () => { + describe('defining (style)', () => { + it('has "style" as a keyword, an id, and can set a css style attribute', function () { + stateDiagram.parser.parse(` +stateDiagram-v2 + style id1 background:#bbb;`); + + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + + // const styleClasses = stateDb.getClasses(); + // expect(styleClasses.get('exampleClass').styles.length).toEqual(1); + // expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb'); + }); + + it('has handles multiple ids', function () { + stateDiagram.parser.parse(` +stateDiagram-v2 + style id1,id2 background:#bbb;`); + + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + + // const styleClasses = stateDb.getClasses(); + // expect(styleClasses.get('exampleClass').styles.length).toEqual(1); + // expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb'); + }); + + // it('can define multiple attributes separated by commas', function () { + // stateDiagram.parser.parse( + // 'stateDiagram-v2\n classDef exampleClass background:#bbb, font-weight:bold, font-style:italic;' + // ); + // stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + + // const styleClasses = stateDb.getClasses(); + // expect(styleClasses.get('exampleClass').styles.length).toEqual(3); + // expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb'); + // expect(styleClasses.get('exampleClass').styles[1]).toEqual('font-weight:bold'); + // expect(styleClasses.get('exampleClass').styles[2]).toEqual('font-style:italic'); + // }); + }); + }); }); diff --git a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison index 44235ecd4..e3bc51235 100644 --- a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison +++ b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison @@ -27,6 +27,13 @@ %x CLASSDEFID %x CLASS %x CLASS_STYLE + +// Style statement states +%x STYLE +%x STYLE_IDS +%x STYLEDEF_STYLES +%x STYLEDEF_STYLEOPTS + %x NOTE %x NOTE_ID %x NOTE_TEXT @@ -75,6 +82,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili (\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' } [^\n]* { this.popState(); return 'STYLECLASS' } +"style"\s+ { this.pushState('STYLE'); return 'style'; } + -
-flowchart LR
+    

Case 1

+
+
+stateDiagram-v2
+AState: Should NOT be white
+BState
+classDef exampleStyleClass fill:#fff,color: blue;
+class AState,BState exampleStyleClass
+style AState fill:#636,border:1px solid red,color:white;
+      
+
+        %%{init: {"look": "classic"} }%%
+stateDiagram-v2
+AState: Should NOT be white
+BState
+classDef exampleStyleClass fill:#fff,color: blue;
+class AState,BState exampleStyleClass
+style AState fill:#636,border:1px solid red,color:white;
+      
+
+stateDiagram-v2
 
-    Apa --AA--> C
-      
+classDef exampleStyleClass background:#bbb,border:1px solid red; +a --> b +class a exampleStyleClass +%% a:::exampleStyleClass +
+
+stateDiagram
+   direction TB
+
+   accTitle: This is the accessible title
+   accDescr: This is an accessible description
+
+   classDef notMoving fill:white
+   classDef movement font-style:italic;
+   classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
+
+   [*] --> Still:::notMoving
+   Still --> [*]
+   Still --> Moving:::movement
+   Moving --> Still
+   Moving --> Crash:::movement
+   Crash:::badBadEvent --> [*]
+      
+
+stateDiagram-v2
+    MyState
+    note left of MyState : I am a leftie
+    note right of MyState : I am a rightie
+
+      
+
+stateDiagram
+%% direction LR
+
+        state C0 {
+          A0 --> B0
+        }
+
+      C0 --> Apa0
+
+      
+
+stateDiagram
+direction LR
+
+
+        state C1 {
+          A1 --> B1
+        }
+
+      C1 --> Apa1
+
+      
+ +

Case 2

+
+
+stateDiagram
+direction LR
+      state Gorilla0 {
+        state Apa0 {
+          A0 --> B0
+        }
+
+      }
+      Apa --> Gorilla0:Label
+      A0 --> C0
+      %% C1: "`This is C`"
+      
+
+stateDiagram
+direction LR
+        state Apa1 {
+          A1
+        }
+
+      Apa11 --> Apa1
+      A1 --> C1
+      %% C1: "`This is C`"
+      
+
+stateDiagram
+    [*] --> Level1
+
+    state Level1 {
+        [*] --> Level2
+
+        state Level2 {
+            [*] --> level2
+            level2 --> Level3
+
+            state Level3 {
+                [*] --> level3
+                level3 --> [*]
+            }
+        }
+    }
+      
+
 flowchart LR
 subgraph Apa["Apa"]
@@ -127,20 +242,7 @@ Apa --> C
 A --> B & C["C"]
       
-
-stateDiagram
-direction LR
-      state Gorilla0 {
-        state Apa0 {
-          A0 --> B0
-        }
 
-      }
-      Apa0 --> C0
-      A0 --> C0
-      C1: "`This is C`"
-      
 flowchart LR
     subgraph Gorilla
@@ -180,7 +282,7 @@ stateDiagram
 
       
-
+    
 flowchart LR
     Apa --Hello--> C
 
@@ -318,7 +420,7 @@ stateDiagram-v2
       mermaid.initialize({
         // theme: 'base',
         // handdrawnSeed: 12,
-        // look: 'handdrawn',
+        look: 'handdrawn',
         // 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
         // layout: 'dagre',
         // layout: 'elk',
diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
index 48a8b7e1b..fed63c444 100644
--- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
+++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
@@ -221,41 +221,46 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
   describe('style statement for a state (style)', () => {
     describe('defining (style)', () => {
       it('has "style" as a keyword, an id, and can set a css style attribute', function () {
-        stateDiagram.parser.parse(`
-stateDiagram-v2
-  style id1 background:#bbb;`);
-
+        stateDiagram.parser.parse(`stateDiagram-v2
+        id1
+        style id1 background:#bbb`);
         stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
+        const data4Layout = stateDiagram.parser.yy.getData();
 
-        // const styleClasses = stateDb.getClasses();
-        // expect(styleClasses.get('exampleClass').styles.length).toEqual(1);
-        // expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb');
+        expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
+      });
+      it('has "style" as a keyword, an id, and can set a css style attribute', function () {
+        stateDiagram.parser.parse(`stateDiagram-v2
+        id1
+        id2
+        style id1,id2 background:#bbb`);
+        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
+        const data4Layout = stateDiagram.parser.yy.getData();
+
+        expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
+        expect(data4Layout.nodes[1].cssStyles).toEqual(['background:#bbb']);
       });
 
-      it('has handles multiple ids', function () {
-        stateDiagram.parser.parse(`
-stateDiagram-v2
-  style id1,id2 background:#bbb;`);
+      it('can define multiple attributes separated by commas', function () {
+        stateDiagram.parser.parse(`stateDiagram-v2
+        id1
+        id2
+        style id1,id2 background:#bbb, font-weight:bold, font-style:italic;`);
 
         stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
+        const data4Layout = stateDiagram.parser.yy.getData();
 
-        // const styleClasses = stateDb.getClasses();
-        // expect(styleClasses.get('exampleClass').styles.length).toEqual(1);
-        // expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb');
+        expect(data4Layout.nodes[0].cssStyles).toEqual([
+          'background:#bbb',
+          'font-weight:bold',
+          'font-style:italic',
+        ]);
+        expect(data4Layout.nodes[1].cssStyles).toEqual([
+          'background:#bbb',
+          'font-weight:bold',
+          'font-style:italic',
+        ]);
       });
-
-      // it('can define multiple attributes separated by commas', function () {
-      //   stateDiagram.parser.parse(
-      //     'stateDiagram-v2\n classDef exampleClass background:#bbb, font-weight:bold, font-style:italic;'
-      //   );
-      //   stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
-
-      //   const styleClasses = stateDb.getClasses();
-      //   expect(styleClasses.get('exampleClass').styles.length).toEqual(3);
-      //   expect(styleClasses.get('exampleClass').styles[0]).toEqual('background:#bbb');
-      //   expect(styleClasses.get('exampleClass').styles[1]).toEqual('font-weight:bold');
-      //   expect(styleClasses.get('exampleClass').styles[2]).toEqual('font-style:italic');
-      // });
     });
   });
 });
diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index 012a64ebd..4ecc1690c 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -202,13 +202,13 @@ const extract = (_doc) => {
           ids.forEach((id) => {
             const state = getState(id);
             if (state !== undefined) {
-              state.styles = styles;
+              state.styles = styles.map((s) => s.replace(/;/g, '')?.trim());
             }
           });
         }
         break;
       case STMT_APPLYCLASS:
-        setStyle(item.id.trim(), item.styleClass);
+        setCssClass(item.id.trim(), item.styleClass);
         break;
     }
   });
diff --git a/packages/mermaid/src/rendering-util/createText.ts b/packages/mermaid/src/rendering-util/createText.ts
index e8fb04fb9..a7ccad650 100644
--- a/packages/mermaid/src/rendering-util/createText.ts
+++ b/packages/mermaid/src/rendering-util/createText.ts
@@ -198,7 +198,16 @@ export const createText = (
   } = {},
   config: MermaidConfig
 ) => {
-  log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
+  log.info(
+    'IPI createText',
+    text,
+    style,
+    isTitle,
+    classes,
+    useHtmlLabels,
+    isNode,
+    addSvgBackground
+  );
   if (useHtmlLabels) {
     // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
 
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts
index b14db3fff..5f8162566 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts
@@ -1,6 +1,5 @@
 import { getConfig } from '$root/diagram-api/diagramAPI.js';
 import type { Node } from '$root/rendering-util/types.d.ts';
-import styles from '../../../../dist/diagrams/packet/styles';
 
 // Striped fill like start or fork nodes in state diagrams
 export const solidStateFill = (color: string) => {
@@ -22,31 +21,33 @@ export const compileStyles = (node: Node) => {
   // node.cssStyles is an array of styles directly set on the node
 
   // concat the arrays and remove duplicates such that the values from node.cssStyles are used if there are duplicates
-  return [...(node.cssCompiledStyles || []), ...(node.cssStyles || [])];
+  const stylesMap = styles2Map([...(node.cssCompiledStyles || []), ...(node.cssStyles || [])]);
+  return { stylesMap, stylesArray: [...stylesMap] };
 };
 
 export const styles2Map = (styles: string[]) => {
-  const styleMap = new Map();
+  const styleMap = new Map();
   styles.forEach((style) => {
     const [key, value] = style.split(':');
-    styleMap.set(key.trim(), value.trim());
+    styleMap.set(key.trim(), value?.trim());
   });
   return styleMap;
 };
 
 export const styles2String = (node: Node) => {
-  const styles = compileStyles(node);
+  const { stylesArray } = compileStyles(node);
   const labelStyles: string[] = [];
   const nodeStyles: string[] = [];
 
-  styles.forEach((style) => {
-    const [key, value] = style.split(':');
+  stylesArray.forEach((style) => {
+    const key = style[0];
     if (key === 'color') {
-      labelStyles.push(style);
+      labelStyles.push(style.join(':') + ' !important');
     } else {
-      nodeStyles.push(style);
+      nodeStyles.push(style.join(':') + ' !important');
     }
   });
+
   return { labelStyles: labelStyles.join(';'), nodeStyles: nodeStyles.join(';') };
 };
 
@@ -55,18 +56,16 @@ export const styles2String = (node: Node) => {
 export const userNodeOverrides = (node: Node, options: any) => {
   const { themeVariables, handdrawnSeed } = getConfig();
   const { nodeBorder, mainBkg } = themeVariables;
-  const styles = compileStyles(node);
+  const { stylesArray: styles, stylesMap } = compileStyles(node);
 
   // index the style array to a map object
-  const styleMap = styles2Map(styles);
-
   const result = Object.assign(
     {
       roughness: 0.7,
-      fill: styleMap.get('fill') || mainBkg,
+      fill: stylesMap.get('fill') || mainBkg,
       fillStyle: 'hachure', // solid fill
       fillWeight: 3.5,
-      stroke: styleMap.get('stroke') || nodeBorder,
+      stroke: stylesMap.get('stroke') || nodeBorder,
       seed: handdrawnSeed,
       strokeWidth: 1.3,
     },