Repository: microsoft/griffel (ts)

Issue:
CSP nonce
Hi team, how to set the nonce for CSPpolicy with griffel？


Expect a global window configuration and an API configuration.
Reference to  the mergeStyles https://github.com/microsoft/fluentui/blob/master/packages/merge-styles/README.md#working-with-content-security-policy-csp

Pull request description:
feat: add "styleElementAttributes" to createDOMRenderer
Fixes #75.

This PR adds `styleElementAttributes` to `createDOMRenderer()`. `styleElementAttributes` is a map of attributes that's passed to the generated style elements. For example, is useful to set ["nonce" attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce).

```js
import { createDOMRenderer } from '@griffel/react';

const renderer = createDOMRenderer(targetDocument, {
  styleElementAttributes: {
    nonce: 'random',
  },
});
```


Patch:
diff --git a/.github/workflows/bundle-size-comment.yml b/.github/workflows/bundle-size-comment.yml
new file mode 100644
index 0000000..8528117
--- /dev/null
+++ b/.github/workflows/bundle-size-comment.yml
@@ -0,0 +1,31 @@
+name: Bundle size | Comment on PR
+on:
+  workflow_run:
+    workflows: ['Bundle size']
+    types:
+      - completed
+
+jobs:
+  comment:
+    runs-on: ubuntu-latest
+    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
+    steps:
+      - name: 'Download artifact'
+        uses: dawidd6/action-download-artifact@v2
+        with:
+          name: monosize-report
+          path: ./results
+          run_id: ${{ github.event.workflow_run.id }}
+          workflow: ${{ github.event.workflow.id }}
+          workflow_conclusion: success
+
+      - name: Load PR number
+        id: pr_number
+        run: echo "::set-output name=id::$(cat pr.txt)"
+        working-directory: ./results
+
+      - name: 'Comment on PR'
+        uses: marocchino/sticky-pull-request-comment@v2
+        with:
+          number: ${{ steps.pr_number.outputs.id }}
+          path: ./results/monosize.md
diff --git a/apps/website/docs/react/api/create-dom-renderer.md b/apps/website/docs/react/api/create-dom-renderer.md
index 794ced1..8844048 100644
--- a/apps/website/docs/react/api/create-dom-renderer.md
+++ b/apps/website/docs/react/api/create-dom-renderer.md
@@ -21,3 +21,17 @@ function App(props) {
   );
 }
 ```
+
+### styleElementAttributes
+
+A map of attributes that's passed to the generated style elements. For example, is useful to set ["nonce" attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce).
+
+```js
+import { createDOMRenderer } from '@griffel/react';
+
+const renderer = createDOMRenderer(targetDocument, {
+  styleElementAttributes: {
+    nonce: 'random',
+  },
+});
+```
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index b80d6bf..4000337 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -92,7 +92,7 @@ jobs:
         displayName: Authenticate git for pushes
 
       - script: |
-          yarn beachball publish -b origin/main --access public -y -n $(npmTokenTmp)
+          yarn beachball publish -b origin/main --access public -y -n $(npmToken)
           git reset --hard origin/main
         env:
           GITHUB_PAT: $(githubPAT)
diff --git a/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json b/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json
new file mode 100644
index 0000000..15ef622
--- /dev/null
+++ b/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: add \"styleElementAttributes\" to createDOMRenderer",
+  "packageName": "@griffel/core",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json b/change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json
new file mode 100644
index 0000000..a699f02
--- /dev/null
+++ b/change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json
@@ -0,0 +1,7 @@
+{
+  "type": "none",
+  "comment": "docs: add docs for createDOMRenderer",
+  "packageName": "@griffel/react",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "none"
+}
diff --git a/packages/core/src/renderer/createDOMRenderer.ts b/packages/core/src/renderer/createDOMRenderer.ts
index 48cb05e..ead0828 100644
--- a/packages/core/src/renderer/createDOMRenderer.ts
+++ b/packages/core/src/renderer/createDOMRenderer.ts
@@ -5,7 +5,14 @@ let lastIndex = 0;
 
 export interface CreateDOMRendererOptions {
   /**
-   * A filter run before css rule insertion to systematically remove css rules at render time.
+   * A map of attributes that's passed to the generated style elements. Is useful to set "nonce" attribute.
+   *
+   * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce
+   */
+  styleElementAttributes?: Record<string, string>;
+
+  /**
+   * A filter run before CSS rule insertion to systematically remove CSS rules at render time.
    * This can be used to forbid specific rules from being written to the style sheet at run time without
    * affecting build time styles.
    *
@@ -35,7 +42,9 @@ export function createDOMRenderer(
       // eslint-disable-next-line guard-for-in
       for (const styleBucketName in cssRules) {
         const cssRulesForBucket = cssRules[styleBucketName as StyleBucketName]!;
-        const sheet = target && getStyleSheetForBucket(styleBucketName as StyleBucketName, target, renderer);
+        const sheet =
+          target &&
+          getStyleSheetForBucket(styleBucketName as StyleBucketName, target, renderer, options.styleElementAttributes);
 
         // This is a hot path in rendering styles: ".length" is cached in "l" var to avoid accesses the property
         for (let i = 0, l = cssRulesForBucket.length; i < l; i++) {
diff --git a/packages/core/src/renderer/getStyleSheetForBucket.ts b/packages/core/src/renderer/getStyleSheetForBucket.ts
index 1783542..9e59742 100644
--- a/packages/core/src/renderer/getStyleSheetForBucket.ts
+++ b/packages/core/src/renderer/getStyleSheetForBucket.ts
@@ -35,6 +35,7 @@ export function getStyleSheetForBucket(
   bucketName: StyleBucketName,
   target: Document,
   renderer: GriffelRenderer,
+  elementAttributes: Record<string, string> = {},
 ): CSSStyleSheet {
   if (!renderer.styleElements[bucketName]) {
     let currentBucketIndex = styleBucketOrdering.indexOf(bucketName) + 1;
@@ -52,8 +53,12 @@ export function getStyleSheetForBucket(
     const tag = target.createElement('style');
 
     tag.dataset['makeStylesBucket'] = bucketName;
-    renderer.styleElements[bucketName] = tag;
 
+    for (const attribute in elementAttributes) {
+      tag.setAttribute(attribute, elementAttributes[attribute]);
+    }
+
+    renderer.styleElements[bucketName] = tag;
     target.head.insertBefore(tag, nextBucketFromCache);
   }
 
diff --git a/packages/react/README.md b/packages/react/README.md
index 4d2616f..0587d98 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -133,6 +133,40 @@ function App() {
 }
 ```
 
+## `createDOMRenderer()`, `RendererProvider`
+
+`createDOMRenderer` is paired with `RendererProvider` component and is useful for child window rendering and SSR scenarios. This is the default renderer for web, and will make sure that styles are injected to a document.
+
+```jsx
+import { createDOMRenderer, RendererProvider } from '@griffel/react';
+
+function App(props) {
+  const { targetDocument } = props;
+  const renderer = React.useMemo(() => createDOMRenderer(targetDocument), [targetDocument]);
+
+  return (
+    <RendererProvider renderer={renderer} targetDocument={targetDocument}>
+      {/* Children components */}
+      {/* ... */}
+    </RendererProvider>
+  );
+}
+```
+
+### styleElementAttributes
+
+A map of attributes that's passed to the generated style elements. For example, is useful to set ["nonce" attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce).
+
+```js
+import { createDOMRenderer } from '@griffel/react';
+
+const renderer = createDOMRenderer(targetDocument, {
+  styleElementAttributes: {
+    nonce: 'random',
+  },
+});
+```
+
 ## Features support
 
 ### 📃 pseudo & class selectors, at-rules, global styles


Test patch:
diff --git a/packages/core/src/renderer/createDOMRenderer.test.ts b/packages/core/src/renderer/createDOMRenderer.test.ts
index 48b8a4f..b2a3aef 100644
--- a/packages/core/src/renderer/createDOMRenderer.test.ts
+++ b/packages/core/src/renderer/createDOMRenderer.test.ts
@@ -49,4 +49,36 @@ describe('createDOMRenderer', () => {
       }
     `);
   });
+
+  it('applies custom attributes to elements', () => {
+    const renderer = createDOMRenderer(document, {
+      styleElementAttributes: {
+        'foo-bar': 'baz',
+        nonce: 'random',
+      },
+    });
+
+    // CSS rules are redundant for this test, but they are necessary as they trigger style nodes creation
+    const cssRules: CSSRulesByBucket = {
+      d: ['.foo { color: red; }'],
+      h: ['.foo:hover { color: blue; }'],
+    };
+
+    renderer.insertCSSRules(cssRules);
+
+    expect(document.head.children).toMatchInlineSnapshot(`
+      HTMLCollection [
+        <style
+          data-make-styles-bucket="d"
+          foo-bar="baz"
+          nonce="random"
+        />,
+        <style
+          data-make-styles-bucket="h"
+          foo-bar="baz"
+          nonce="random"
+        />,
+      ]
+    `);
+  });
 });