diff --git a/src/builder/rect.ts b/src/builder/rect.ts index 49686cdf..01ea0426 100644 --- a/src/builder/rect.ts +++ b/src/builder/rect.ts @@ -20,6 +20,8 @@ export default async function rect( isInheritingTransform, src, debug, + className, + htmlId, }: { id: string left: number @@ -29,6 +31,8 @@ export default async function rect( isInheritingTransform: boolean src?: string debug?: boolean + className?: string + htmlId?: string }, style: Record, inheritableStyle: Record @@ -293,6 +297,9 @@ export default async function rect( ) return ( + `` + (defs ? buildXMLString('defs', {}, defs) : '') + (shadow ? shadow[0] : '') + (imageBorderRadius ? imageBorderRadius[0] : '') + @@ -307,6 +314,7 @@ export default async function rect( (style.transform && (currentClipPath || maskId) ? '' : '') + (opacity !== 1 ? `` : '') + (shadow ? shadow[1] : '') + - extra + extra + + '' ) } diff --git a/src/layout.ts b/src/layout.ts index 70734036..db5d1459 100644 --- a/src/layout.ts +++ b/src/layout.ts @@ -47,6 +47,8 @@ export interface SatoriNode { key?: string | number props: Record textContent?: string + className?: string + htmlId?: string } export default async function* layout( @@ -110,7 +112,14 @@ export default async function* layout( 'dangerouslySetInnerHTML property is not supported. See documentation for more information https://github.com/vercel/satori#jsx.' ) } - let { style, children, tw, lang: _newLocale = locale } = props || {} + let { + style, + children, + tw, + lang: _newLocale = locale, + className, + id: htmlId, + } = props || {} const newLocale = normalizeLocale(_newLocale) // Extend Tailwind styles. @@ -217,6 +226,8 @@ export default async function* layout( props: restProps, key: element.key, textContent: isReactElement(childrenNode) ? undefined : childrenNode, + className, + htmlId, }) // Generate the rendered markup for the current node. @@ -232,6 +243,8 @@ export default async function* layout( src, isInheritingTransform, debug, + className, + htmlId, }, computedStyle, newInheritableStyle @@ -251,6 +264,8 @@ export default async function* layout( src, isInheritingTransform, debug, + className, + htmlId, }, computedStyle, newInheritableStyle @@ -269,7 +284,17 @@ export default async function* layout( ) } baseRenderResult = await rect( - { id, left, top, width, height, isInheritingTransform, debug }, + { + id, + left, + top, + width, + height, + isInheritingTransform, + debug, + className, + htmlId, + }, computedStyle, newInheritableStyle ) diff --git a/test/event.test.tsx b/test/event.test.tsx index 0b869532..9e22b57e 100644 --- a/test/event.test.tsx +++ b/test/event.test.tsx @@ -26,7 +26,9 @@ describe('Event', () => { expect(nodes).toMatchInlineSnapshot(` [ { + "className": undefined, "height": 50, + "htmlId": undefined, "key": null, "left": 0, "props": { @@ -42,7 +44,9 @@ describe('Event', () => { "width": 100, }, { + "className": undefined, "height": 50, + "htmlId": undefined, "key": null, "left": 0, "props": {}, @@ -52,7 +56,9 @@ describe('Event', () => { "width": 37, }, { + "className": undefined, "height": 50, + "htmlId": undefined, "key": null, "left": 37, "props": {}, diff --git a/test/id-and-class.test.tsx b/test/id-and-class.test.tsx new file mode 100644 index 00000000..e67b6de9 --- /dev/null +++ b/test/id-and-class.test.tsx @@ -0,0 +1,23 @@ +import { it, describe, expect } from 'vitest' + +import { initFonts } from './utils.js' +import satori from '../src/index.js' + +describe('Id and Class', () => { + let fonts + initFonts((f) => (fonts = f)) + + it('should preserve id and class attributes', async () => { + const svg = await satori( +
, + { + width: 100, + height: 100, + fonts, + } + ) + expect(svg).toMatchInlineSnapshot( + '""' + ) + }) +})