mirror of
https://github.com/documenso/documenso.git
synced 2026-07-08 03:55:07 +10:00
0b86ece1d5
Platform-plan organisations and teams can now customise non-embed signing pages with six brand colour tokens, a border-radius, and a free-text custom CSS block (up to 256 KB). - Stored on OrganisationGlobalSettings / TeamGlobalSettings; teams inherit from the org via brandingEnabled === null. - CSS is sanitised on save (PostCSS) so we can inline it at SSR with no per-render parsing. - Rendered via a nonce'd <style> scoped under .documenso-branded, using native CSS nesting so user selectors don't need scoping. - Gated on the existing embedSigningWhiteLabel claim (or self-hosted) — reuses the embed white-label decision.
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { CSS_LENGTH_REGEX, type TCssVarsSchema } from '@documenso/lib/types/css-vars';
|
|
import { colord } from 'colord';
|
|
import { toKebabCase } from 'remeda';
|
|
|
|
export const toNativeCssVars = (vars: TCssVarsSchema) => {
|
|
const cssVars: Record<string, string> = {};
|
|
|
|
const { radius, ...colorVars } = vars;
|
|
|
|
for (const [key, value] of Object.entries(colorVars)) {
|
|
if (value) {
|
|
const color = colord(value);
|
|
const { h, s, l } = color.toHsl();
|
|
|
|
// Tailwind's theme.css consumes these via `hsl(var(--token))`. CSS
|
|
// Color 4 space-separated `hsl()` requires `%` on saturation and
|
|
// lightness — without it, the function is invalid and the property
|
|
// falls back to its initial value (which is why bare numeric output
|
|
// here used to silently break customer colours).
|
|
cssVars[`--${toKebabCase(key)}`] = `${h} ${s}% ${l}%`;
|
|
}
|
|
}
|
|
|
|
// Defence in depth: radius is interpolated raw into the rendered <style>
|
|
// block, so anything outside the length pattern is a CSS-injection vector.
|
|
// The Zod schema rejects bad values at the API boundary; this re-check
|
|
// protects against schema drift and any path that bypasses validation.
|
|
if (radius && CSS_LENGTH_REGEX.test(radius)) {
|
|
cssVars[`--radius`] = radius;
|
|
}
|
|
|
|
return cssVars;
|
|
};
|
|
|
|
/**
|
|
* Pure-string sibling of `toNativeCssVars` — returns the same set of CSS custom
|
|
* property declarations as a single string suitable for SSR inlining inside a
|
|
* rule block. Does not touch the DOM.
|
|
*
|
|
* Example: { background: '#111', radius: '0.5rem' }
|
|
* -> "--background: 0 0% 6.7%; --radius: 0.5rem;"
|
|
*
|
|
* Saturation and lightness include the `%` suffix that
|
|
* `hsl(var(--token))` requires under CSS Color 4 space-separated syntax.
|
|
*/
|
|
export const toNativeCssVarsString = (vars: TCssVarsSchema): string => {
|
|
const map = toNativeCssVars(vars);
|
|
return Object.entries(map)
|
|
.map(([k, v]) => `${k}: ${v};`)
|
|
.join(' ');
|
|
};
|
|
|
|
export const injectCss = (options: { css?: string; cssVars?: TCssVarsSchema }) => {
|
|
const { css, cssVars } = options;
|
|
|
|
if (css) {
|
|
const style = document.createElement('style');
|
|
style.textContent = css;
|
|
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
if (cssVars) {
|
|
const nativeVars = toNativeCssVars(cssVars);
|
|
|
|
for (const [key, value] of Object.entries(nativeVars)) {
|
|
document.documentElement.style.setProperty(key, value);
|
|
}
|
|
}
|
|
};
|