
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
@soyio/soyio-widget
Advanced tools
Our SDK for web based integrations
Install using npm! (or your favorite package manager)
# Using npm
npm install @soyio/soyio-widget
# Using yarn
yarn add @soyio/soyio-widget
# Using pnpm
pnpm add @soyio/soyio-widget
Integrate the widget into your frontend framework using the script below. Ensure to replace placeholders (e.g., <request>, <company id>) with actual values relevant to your implementation.
The Soyio Widget provides several modules that you can integrate into your application:
The ConsentBox is a component that allows you to embed a consent request directly within your webpage, rather than opening it in a popup window. This is particularly useful when you want to integrate the consent flow seamlessly into your application's interface.
<!-- Add a container div where the consent request will be mounted -->
<div id="consent-request-box"></div>
<script>
import { ConsentBox } from "@soyio/soyio-widget";
// Configuration for the consent request
const consentOptions = {
consentTemplateId: "<consent template id>",
onEvent: (data) => console.log(data),
isSandbox: true, // Optional
appearance: {}, // Optional
actionToken: "<action token>", // Optional
entityId: "<entity id>", // Optional
context: "<context>", // Optional
onReady: () => console.log("ConsentBox is ready"), // Optional
optionalReconsentBehavior: "notice", // Optional
mandatoryReconsentBehavior: "notice", // Optional
allowGranularScopeSelection: true, // Optional
metadata: { key: "value" }, // Optional
};
// Wait for DOM to be fully loaded
document.addEventListener("DOMContentLoaded", () => {
// Create and mount the consent request box
const consentBox = new ConsentBox(consentOptions);
consentBox.mount("#consent-request-box");
});
</script>
The onEvent follows the following format:
{
eventName: 'CONSENT_CHECKBOX_CHANGE',
isSelected: boolean,
actionToken: string,
}
getState(): Returns the current state of the consent box. The returned object has the following structure:{
isSelected: boolean,
actionToken: string | null,
}
consentTemplateId: Identifier of consent template. It must start with 'constpl_'.
isSelected: Boolean value indicating whether the consent checkbox is selected or not.
appearance: Customize the appearance of the iframe. Learn more.
actionToken: In case of losing the state of the consent (i.e. page reload), you can use a previously generated actionToken to restore the state of the consent.
entityId: Identifier of the entity associated with a ConsentAction. If provided and a consent was previously granted by this entity, the UI will display a message indicating that consent has already been given.
origin: Additional information that will be saved with the consent. Useful when you want to track the consent from a specific origin.
onReady: Optional callback that executes when the consent box is ready to use. You can use this to handle logic when the iframe is not mounted yet.
optionalReconsentBehavior: What should happen when the consent is initialized with an entityId that has already given consent on an optional category.
notice will show a message letting the user know that they have already given consent,askAgain will show the consent as if it wasn't given in the first place,hide will not show the consent at all.We strongly recommend using notice so the user doesn't have to give the consent again and knows what they have already given consent to.
mandatoryReconsentBehavior: What should happen when the consent is initialized with an entityId that has already given consent on a mandatory category.
notice will show a message letting the user know that they have already given consent,askAgain will show the consent as if it wasn't given in the first place,We don't support hiding the mandatory consent, and we strongly recommend using notice so the user doesn't have to give the consent again and knows what they have already given consent to.
allowGranularScopeSelection: Enables selecting consent at a more granular scope when the template supports granular scopes.
metadata: An optional object with arbitrary metadata to attach to the consent. This metadata will be included in the consent record. The object must be serializable to JSON; properties with non-serializable values (like functions or undefined) will be omitted.
The PrivacyCenterBox lets you embed the Privacy Center inside your page. You can scope which features to show and which data subjects are relevant to your interface. For more info check our docs.
<!-- Add a container div where the Privacy Center will be mounted -->
<div id="privacy-center-box"></div>
<script>
import { PrivacyCenterBox } from "@soyio/soyio-widget";
// Configuration for the Privacy Center
const privacyCenterOptions = {
// Choose ONE of the following authentication modes:
// 1) Public mode
companyId: "<company id>", // e.g. com_...
// 2) Authenticated mode
// sessionToken: "<session token>",
// Feature flags (optional)
enabledFeatures: ["DataSubjectRequest", "ConsentManagement"],
// DSR rights to show (optional)
// When omitted, the DSR form will use arsop right as default
enabledRights: ["arsop"],
// Request reference (optional)
// Will be attached to data subject requests
requestReference: "<reference>", // e.g. some uuid or id to match our created records with your frontend flows
// Limit consent view to specific data subjects (optional)
dataSubjects: ["customer", "employee"],
// File upload configuration (optional)
fileRequisites: {
allowedExtensions: ["pdf", "png", "jpeg", "jpg"],
maxFileSize: 5 * 1024 * 1024, // 5MB
},
// Redec operation options (optional unless "redec" is in enabledRights)
redecOperationIds: [
{ id: "op_update", label: "Update" },
{ id: "op_rectification", label: "Rectification" },
],
// Override the "exercise your rights" link shown in consent-only mode (optional)
externalRightsExerciseUrl: "https://privacy.example.com/rights",
// Header and copy customization (optional)
content: {
header: {
title: "Privacy preferences",
description: "Manage your requests and consent settings.",
},
consentManagement: {
header: {
title: "Manage your consent settings",
description: "Select which permissions should remain active.",
},
},
rightExamples: {
access: 'Example: "I want to know what data you store about me."',
},
},
// Consent management grouping customization (optional)
consentManagement: {
scopeGroups: [
{
title: "Financial products",
scopes: [
{ scopeType: "product", scopeId: "prod_checking_account" },
{ scopeType: "product", scopeId: "prod_credit_card" },
],
},
{
title: "Main branches",
scopes: [
{ scopeType: "branch", scopeId: "branch_centro" },
{ scopeType: "branch", scopeId: "branch_providencia" },
],
},
],
},
// Common options
consentControl: "checkbox", // Optional: 'switch' (default) | 'checkbox'
consentMode: "batch", // Optional: 'immediate' (default) | 'batch'
showBatchConsentConfirmation: true, // Optional: only used in batch mode
consentRetentionPeriod: "30 days", // Optional: prevents immediate revoke for this period
allowGranularScopeSelection: true, // Optional: only applies when templates have multiple scopes
groupConsentsByScope: true, // Optional: groups consent templates by scope in consent management
onEvent: (event) => console.log(event),
onReady: () => console.log("PrivacyCenterBox is ready"), // Optional
isSandbox: true, // Optional
appearance: {
config: {
showHeader: true,
showConsentManagementHeader: true,
},
}, // Optional
};
// Wait for DOM to be fully loaded
document.addEventListener("DOMContentLoaded", () => {
const privacyCenter = new PrivacyCenterBox(privacyCenterOptions);
privacyCenter.mount("#privacy-center-box");
});
</script>
sessionToken: Use this to authenticate a session directly.companyId: The company identifier. Must start with com_. Use this when Privacy Center is mounted in a non authenticated environment.enabledFeatures: Optional array of features to show. Supported values: "DataSubjectRequest", "ConsentManagement".enabledRights: Optional array of rights for the Data Subject Request (DSR) form. Supported values: "arsop", "redec". When multiple values are provided, the form starts with a right selection step. When a single value is provided, the selection step is skipped. When omitted, the form defaults to arsop rights.requestReference: Optional string, intended to be a reference of the current session. It will be attached to created data subject requests.dataSubjects: Optional array of data subject categories. When present, the consent management view only shows consent for the specified categories. Supported values include: "anonymous_user", "citizen_voter", "commuter", "consultant", "customer", "employee", "job_applicant", "next_of_kin", "passenger", "patient", "prospect", "shareholder", "supplier_vendor", "trainee", "visitor".fileRequisites: Optional object to configure file upload constraints.
allowedExtensions: Array of allowed file extensions (e.g. ['pdf', 'jpg']). Default: ['pdf', 'png', 'jpeg', 'jpg'].maxFileSize: Maximum file size in bytes. Default: 5 * 1024 * 1024 (5MB).redecOperationIds: Optional array of { id, label } values for the Redec operation select. Required if redec right is included in enabledRights param.externalRightsExerciseUrl: Optional URL used by required consent alerts when users need to exercise their rights outside the embedded privacy center.appearance.config.showHeader: Optional boolean to show/hide the privacy center header (title and description).content.header: Optional object with title and description overrides for the privacy center header copy.appearance.config.showConsentManagementHeader: Optional boolean to show/hide the title and description in the consent management page.content.consentManagement.header: Optional object with title and description overrides for the consent management header copy.content.rightExamples: Optional object to override DSR right examples. Supported keys: access, opposition, rectification, suppression, portability, redec_update, redec_rectification, redec_complementation, redec_cancellation.header: Optional legacy alias for content.header.rightExamples: Optional legacy alias for content.rightExamples.isSandbox: Whether to use the sandbox environment. Defaults to false.consentControl: Optional, controls the visual interaction for consent toggles. Values: 'switch' (default) or 'checkbox'.consentMode: Optional, controls how consent changes are committed. Values: 'immediate' (default) or 'batch' (save multiple changes at once).consentRetentionPeriod: Optional, specifies a duration during which a consent cannot be revoked after being granted. Format: "<value> <unit>". Supported units: day, week, month, year (and their plural forms). Example: '30 days', '1 week'.allowGranularScopeSelection: Optional boolean, enables selecting/deselecting individual consent scopes in consent management when templates define multiple scopes. When enabled and a consent has more than one scope, the main consent checkbox supports partial state.groupConsentsByScope: Optional boolean, groups consent templates by scope in consent management. This must be enabled for consentManagement.scopeGroups to apply.consentManagement.scopeGroups: Optional array to customize accordion groups in consent management. Each group requires a title and a scopes array with { scopeType: 'product' | 'branch', scopeId: string } entries. Scopes not included in custom groups keep the default scope grouping.showBatchConsentConfirmation: Optional boolean, whether to show a confirmation dialog before saving consent changes in batch mode.appearance: Customize the iframe appearance. See Appearance section below.onEvent: Callback that receives events from the iframe.onReady: Optional callback fired when the iframe becomes ready.Note:
sessionToken is provided, do not pass companyId.consentManagement.scopeGroups is applied during initial iframe URL render and also synchronized through SET_PRIVACY_CENTER_CONFIG updates.content.header, content.consentManagement.header, appearance.config.showConsentManagementHeader, and externalRightsExerciseUrl are applied through SET_PRIVACY_CENTER_CONFIG; those overrides may briefly appear after the iframe is ready.REQUEST_SUBMITTED: This event occurs when a user successfully submits a Data Subject Request. The event object includes:
eventName: The name of the event, in this case, 'REQUEST_SUBMITTED'.kind: The kind of the Data Subject Request submitted. Supported values are: access, opposition, rectification, suppression and portability[!NOTE] In Safari browsers, disclosure requests can only be opened as a result of a direct user interaction (like a click event). This is due to Safari's security policies regarding popup windows. Always ensure the disclosure request initialization is triggered by a user action when supporting Safari browsers.
A disclosure_request is a process that a user goes through where they are verified, and then they share the necessary data as required by each company.
This verification can happen in one of the following two ways:
Validation: Through document validation and facial video. This occurs when a user has never been verified before with Soyio.
Authentication: Through an access key (passkey) or facial video. This can occur when a user has already been validated previously with Soyio.
To instantiate this process in the code, you have two options:
This doesn't require any previous setup. Given your company and disclosure template IDs, you can create disclosure requests freely when the user starts the widget.
<button id="start-disclosure-request">Start disclosure request</button>
<script>
import { SoyioWidget } from "@soyio/soyio-widget";
// Widget configuration
const widgetConfig = {
request: "disclosure",
configProps: {
companyId: "<company id>",
userReference: "<user identifier of company>",
userEmail: "<user email>",
templateId: "<template id>",
customColor: "<custom color>",
},
onEvent: (data) => console.log(data),
isSandbox: true,
};
// Function to create the widget
function initWidget() {
new SoyioWidget(widgetConfig);
}
// Add event listener to the button to create the widget on click
document
.getElementById("start-disclosure-request")
.addEventListener("click", initWidget);
</script>
Optional props:
userEmailcustomColor.You can alternatively create a disclosure request beforehand with some matchers to make sure the person completing the request matches the one that your application thinks it is.
For more details about the use case, please refer to the documentation.
To use this option, simply specify the disclosure request ID along with any optional parameters:
<button id="start-disclosure-request">Start disclosure request</button>
<script>
import { SoyioWidget } from "@soyio/soyio-widget";
// Widget configuration
const widgetConfig = {
request: "disclosure",
configProps: {
disclosureRequestId: "<disclosure request id>",
customColor: "<custom color>",
},
onEvent: (data) => console.log(data),
isSandbox: true,
};
// Function to create the widget
function initWidget() {
new SoyioWidget(widgetConfig);
}
// Add event listener to the button to create the widget on click
document
.getElementById("start-disclosure-request")
.addEventListener("click", initWidget);
</script>
Optional properties:
customColorNote: User and template properties are not specified here because they must be specified when creating the disclosure request beforehand.
The onEvent callback is designed to handle various events that occur during widget interaction. Specifically, it receives detailed information upon the successful completion of user request. Here are the events it handles:
DISCLOSURE_REQUEST_SUCCESSFUL: This event occurs when a user successfully completes a disclosure_request. The identity verification could have been a validation or authentication.
eventName: The name of the event, in this case, 'DISCLOSURE_REQUEST_SUCCESSFUL'.verificationMethod: Takes the values of authentication or validation.identityId: The unique identifier for the verified identity.userReference: The reference identifier for the user, facilitating the association of the event with the user within the company's context.DENIED_CAMERA_PERMISSION: Event triggered when user denies camera permissions. It closes the widget.
UNEXPECTED_ERROR: Event triggered when user exits because of an unexpected error.
WIDGET_CLOSED: This event occurs when the user closes the Soyio pop up. The event object is as follows:
{ eventName: 'WIDGET_CLOSED' }.WIDGET_OPENED: This event occurs when the user closes the Soyio pop up. The event object is as follows:
{ eventName: 'WIDGET_CLOSED' }.companyId: The unique identifier for the company, must start with 'com_'.userReference: A reference identifier provided by the company for the user engaging with the widget. This identifier is used in events (onEvent and webhooks) to inform the company which user the events are associated with.userEmail: The user's email address. If not provided, Soyio will prompt the user to enter their email.templateId: Identifier of template. Specifies the order and quantity of documents requested from the user, as well as the mandatory data that the user is asked to share with the company. It must start with 'dtpl_'.disclosureRequestId: If created beforehand, you can target a specific disclosure request that the user must complete. It is useful if you need to match some data between the disclosure process and your database records. It must start with 'dreq_'identityId: This identifier must start with 'id_' and signifies the user's identity.isSandbox: Indicates if the widget should operate in sandbox mode, defaulting to false.onEvent: A callback function triggered upon event occurrences, used for capturing and logging event-related data.customColor: A hex code string that specifies the base color of the interface.Use DisclosureRequestBox when you want to keep the disclosure flow inside your page, without popup windows.
This embedded API currently supports disclosure flows created in advance (existing disclosure requests only).
<div id="embedded-disclosure"></div>
<script>
import { DisclosureRequestBox } from "@soyio/soyio-widget";
const widget = new DisclosureRequestBox({
disclosureRequestId: "<disclosure request id>",
customColor: "#0F172A", // Optional
onEvent: (event) => {
console.log("DisclosureRequestBox event:", event);
},
onReady: () => {
console.log("DisclosureRequestBox is ready");
},
minHeight: "640px", // Optional
height: "720px", // Optional
isSandbox: true, // Optional
});
widget.mount("#embedded-disclosure");
// Optional cleanup
// widget.unmount();
</script>
Passkey registration and passkey authentication are handled through a temporary popup tunnel and then return control to the embedded iframe flow.
The signature_attempt is a process where, using a previously created signature_attempt_id, a request is initiated in which a user can digitally sign a document. To sign the document, the user must be authenticated. This authentication can occur either through an access key or facial video. It's important to note that for this request, the user must have been previously verified with Soyio.
<button id="start-signature-attempt">Start signature attempt</button>
<script>
import { SoyioWidget } from "@soyio/soyio-widget";
// Widget configuration
const widgetConfig = {
request: "signature",
configProps: {
signatureAttemptId: "<signature attempt id>",
customColor: "<custom color>",
},
onEvent: (data) => console.log(data),
isSandbox: true,
};
// Function to create the widget
function initWidget() {
new SoyioWidget(widgetConfig);
}
// Add event listener to the button to create the widget on click
document
.getElementById("start-signature-attempt")
.addEventListener("click", initWidget);
</script>
Optional props:
customColor.IDENTITY_SIGNATURE: This event occurs when a user successfully completes a signature attempt. The event object includes:
eventName: The name of the event, in this case, 'IDENTITY_SIGNATURE'.userReference: The reference identifier for the user, facilitating the association of the event with the user within the company's context.REJECTED_SIGNATURE: Event triggered when user clicks the "reject" button in the signature attempt. The event object includes:
identityId: The unique identifier for the identity.userReference: The userReference used in the validation attempt for the identity.signatureAttemptId: Identifier of signature attempt obtained when creating the SignatureAttempt. It must start with 'sa_'.isSandbox: Indicates if the widget should operate in sandbox mode, defaulting to false.onEvent: A callback function triggered upon event occurrences, used for capturing and logging event-related data.customColor: A hex code string that specifies the base color of the interface.The auth_request is a process where, using a previously created auth_request_id, a request is initiated in which a user can authenticate. This authentication can occur either through an access key or facial video. It's important to note that for this request, the user must have been previously verified with Soyio.
<button id="start-auth-request">Start auth request</button>
<script>
import { SoyioWidget } from "@soyio/soyio-widget";
// Widget configuration
const widgetConfig = {
request: "authentication_request",
configProps: {
authRequestId: "<auth request id>",
customColor: "<custom color>",
},
onEvent: (data) => console.log(data),
isSandbox: true,
};
// Function to create the widget
function initWidget() {
new SoyioWidget(widgetConfig);
}
// Add event listener to the button to create the widget on click
document
.getElementById("start-auth-request")
.addEventListener("click", initWidget);
</script>
Optional props:
customColor.authRequestId: Identifier of auth request obtained when creating the AuthRequest. It must start with 'authreq_'.isSandbox: Indicates if the widget should operate in sandbox mode, defaulting to false.onEvent: A callback function triggered upon event occurrences, used for capturing and logging event-related data.customColor: A hex code string that specifies the base color of the interface.Customize the look and feel of Soyio UI components by passing an appearance object to the configuration. The appearance object supports themes, CSS variables, and CSS rules for granular control over the styling.
The appearance object consists of four main sections:
const appearance = {
theme: string,
variables: Variables,
rules: Rules,
config: Config,
};
Built-in themes provide pre-configured color palettes and component styles:
| Theme | Description |
|---|---|
"soyio" | Default light theme with Soyio brand colors (purple/indigo), uppercase titles |
"flat" | Minimal theme with square corners, normal-case titles, thicker borders |
"neutral" | Neutral palette that works well for both light and dark modes |
"night" | Deprecated alias for theme: "soyio" with mode: "dark" |
Theme style differences:
Appearance now supports a mode field:
| Mode | Description |
|---|---|
"light" | Forces light mode |
"dark" | Forces dark mode |
"auto" | Follows the user's system color scheme |
Example:
const appearance = {
theme: "soyio",
mode: "dark",
variables: {
// You can still override specific variables
colorPrimary: "#FF6B6B",
},
rules: {
// You can also override theme rules
".CardTitle": { fontWeight: "700" },
},
};
Theme variables and rules are applied first, then your custom overrides take precedence.
theme: "night"is still accepted as a deprecated alias for backwards compatibility, but new integrations should usetheme + mode.
The config object allows you to adjust component behavior settings.
interface Config {
helperTextPosition?: 'top' | 'bottom';
hintIcon?: string;
icon?: {
weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone';
size?: number;
};
iconRules?: Record<string, { weight?: IconWeight; size?: number }>;
mainPageColumns?: 1 | 2 | 3 | 4;
brandTheme?: 'default' | 'dark' | 'light';
}
| Property | Description | Default |
|---|---|---|
helperTextPosition | Position of helper/description text relative to form inputs | "bottom" |
hintIcon | Icon name for hint/help tooltips on input labels (see available icons below) | "Question" |
icon.weight | Global icon weight/style variant (see below) | "regular" |
icon.size | Global default icon size in pixels | 24 |
iconRules | Per-component icon style overrides | {} |
mainPageColumns | Number of columns in the main page feature cards grid (1-4) | 2 |
consentManagementColumns | Number of columns in the consent management grid (1-4) | 2 |
brandTheme | Theme variant for branded elements like the footer (default, dark, light) | "default" |
Soyio uses Phosphor Icons, a flexible icon family with multiple weight variants. You can customize the icon appearance globally using the config.icon settings, or override icons for specific components using config.iconRules.
Available icon weights:
| Weight | Description |
|---|---|
thin | Thinnest stroke width |
light | Light stroke width |
regular | Default stroke width |
bold | Bold stroke width |
fill | Filled/solid icons |
duotone | Two-tone icons with opacity |
Global icon example:
const appearance = {
config: {
icon: {
weight: "bold",
size: 20,
},
},
};
Per-component icon overrides:
Use iconRules to customize icons for specific components. The key is the component name (e.g., Alert, Switch) or a variant-specific key (e.g., Alert.error):
Note: For variant-specific icon rules, use dot notation (
Alert.error) rather than the CSS double-dash syntax (Alert--error).
const appearance = {
config: {
icon: {
weight: "regular", // Global default
},
iconRules: {
Alert: { weight: "fill" }, // All alerts use filled icons
Switch: { weight: "bold", size: 16 }, // Switch icons are bold and smaller
"Alert.error": { weight: "fill" }, // Error alerts specifically
},
},
};
Hint icon customization:
The hint icon appears next to input labels when a tooltip/hint is available. You can change which icon is displayed using hintIcon:
| Icon Name | Description |
|---|---|
Question | Question mark in circle (default) |
Info | Information "i" icon |
QuestionMark | Simple question mark |
Warning | Warning/exclamation icon |
const appearance = {
config: {
hintIcon: "Info", // Use info icon instead of question mark
},
};
You can also style the hint icon using the .HintIcon rule (see Supported rules).
Use variables to adjust common visual attributes across all components.
interface Variables {
fontFamily?: string;
fontFamilyBody?: string;
fontFamilyTitle?: string;
fontSizeBase?: string;
colorPrimary?: string;
colorPrimarySurface?: string;
colorSecondary?: string;
colorBackground?: string;
colorSurface?: string;
colorSurfaceMuted?: string;
colorSurfaceStrong?: string;
colorBorder?: string;
colorBorderMuted?: string;
colorSwitchBorder?: string;
colorText?: string;
colorTextSecondary?: string;
colorTextSubtle?: string;
colorTextInverted?: string;
colorTextTitle?: string;
colorLink?: string;
colorInputFocus?: string;
colorInputErrorFocus?: string;
colorSelectArrow?: string;
colorInfo?: string;
colorInfoBg?: string;
colorSuccess?: string;
colorSuccessBg?: string;
colorWarning?: string;
colorWarningBg?: string;
colorDanger?: string;
colorDangerBg?: string;
colorOverlay?: string;
borderRadius?: string;
borderWidth?: string;
borderStyle?: string;
}
| Variable | Description | Default |
|---|---|---|
fontFamily | Base font stack (fallback for body and title) | "system-ui, sans-serif" |
fontFamilyBody | Font stack for body/paragraph text (falls back to fontFamily) | var(--fontFamily) |
fontFamilyTitle | Font stack for titles and headings (falls back to fontFamily) | var(--fontFamily) |
fontSizeBase | Base font size for text | "1rem" |
colorPrimary | Primary color for interactive elements | "#0570DE" |
colorPrimarySurface | Background color for primary elements (e.g. active tab) | "#EEF2FF" |
colorSecondary | Secondary color for interactive elements | "#A180F0" |
colorBackground | Background color | "#FFFFFF" |
colorSurface | Surface color for cards and sections | "#F9FAFB" |
colorSurfaceMuted | Muted surface color | "#F3F4F6" |
colorSurfaceStrong | Strong surface color | "#E5E7EB" |
colorBorder | Border color | "#D1D5DB" |
colorBorderMuted | Muted border color | "#E5E7EB" |
colorSwitchBorder | Border color for Switch component (unchecked) | "#000000" |
colorText | Main text color | "#1E1B4B" |
colorTextSecondary | Secondary text color | "#6B7280" |
colorTextSubtle | Subtle text color | "#9CA3AF" |
colorTextInverted | Inverted text color | "#FFFFFF" |
colorTextTitle | Title/heading text color (falls back to colorText) | var(--colorText) |
colorLink | Color for link elements | "#0570DE" |
colorInputFocus | Focus border/ring color for input elements | "#0570DE" |
colorInputErrorFocus | Focus border/ring color for input elements in error state | "#EF4444" |
colorSelectArrow | Color for select dropdown arrow icon | "#6B7280" |
colorInfo | Info status color | "#1E40AF" |
colorInfoBg | Info status background color | "#E0E7FF" |
colorSuccess | Success status color | "#15803D" |
colorSuccessBg | Success status background color | "#DCFCE7" |
colorWarning | Warning status color | "#B45309" |
colorWarningBg | Warning status background color | "#FEF3C7" |
colorDanger | Danger status color | "#EF4444" |
colorDangerBg | Danger status background color | "#FEF2F2" |
colorOverlay | Overlay color | "rgba(0, 0, 0, 0.5)" |
borderRadius | Base border radius (scales proportionally for different sizes) | "0.25rem" |
borderWidth | Border width for elements | "1px" |
borderStyle | Border style for elements | "solid" |
Note on Border Radius: The
borderRadiusvariable serves as a base unit. Larger components (like Inputs and Buttons) use a multiple of this value (typically 2x), while smaller elements use a fraction or the base value itself. This ensures consistent scaling across all UI elements.
The rules object allows you to apply custom CSS to specific elements. Soyio supports styling for various components including inputs, buttons, switches, and more.
The rules are grouped by component category. Most rules support pseudo-classes and pseudo-elements that can be appended to style different states:
Supported pseudo-classes:
:hover - When the element is hovered:focus - When the element is focused:active - When the element is active/pressed:disabled - When the element is disabled:autofill - When the input is autofilled:focus-visible - When focused via keyboard navigationSupported pseudo-elements:
::placeholder - Placeholder text in inputs::selection - Selected textExample usage:
rules: {
".Button": { backgroundColor: "blue" }, // Base style
".Button:hover": { backgroundColor: "darkblue" }, // Hover state
".Button:disabled": { opacity: "0.5" }, // Disabled state
".Input::placeholder": { color: "gray" }, // Placeholder text
".RadioCard:hover": { borderColor: "var(--colorPrimary)" }, // Card hover
}
.MainContainer - The main container..Card - Card containers..CardTitle - Card title text..Dialog - Dialog containers..DialogOverlay - Dialog overlays..DialogContent - Dialog content areas..Title - Title text (base class for all titles)..StepTitle - Step indicator title text (also inherits from .Title)..Description - Description text..Input - Input fields..Input--error - Input fields in error state..Label - Labels..HintIcon - Hint/help icons next to input labels..TextArea - Text area inputs..Select - Select dropdowns..Combobox - Combobox inputs..NinInput - Styles the input field for national identity numbers..TrackingCodeInput - Styles the tracking code input component..TrackingCodeInputCell - Styles individual cells in the tracking code input..TrackingCodeInputSeparator - Styles the separator between tracking code cells..Button - The button component..Link - Links.Checkbox

.Checkbox - The checkbox container..CheckboxInput - The styled checkbox element (supports borderRadius, borderColor, backgroundColor)..CheckboxLabel - The checkbox label..CheckboxCheck - The checkmark icon inside the checkbox..CheckboxInput--checked - The checked state of the checkbox.Radio
.Radio - Radio button containers..RadioButton - The radio button element (the clickable circle)..RadioButton--checked - Checked state of the radio button..RadioIndicator - The inner indicator point of the radio button..RadioIndicator--checked - Checked state of the radio indicator (visible when selected)..RadioLabel - Radio button labels.Switch
.Switch - Switch toggles (wrapper)..SwitchRoot - Switch control (track)..SwitchThumb - Switch thumb (circle)..SwitchIcon - Switch icons (check/cross)..SwitchRoot--checked - Checked state of the switch track.SwitchThumb--checked - Checked state of the switch thumbRadio Card
.RadioCard - Styles the wrapper card element of a radio card item..RadioCard--checked - Checked state of the radio card..RadioCardButton - The radio button element inside a radio card..RadioCardButton--checked - Checked state of the radio card button..RadioCardIndicator - The inner indicator point inside a radio card..RadioCardIndicator--checked - Checked state of the radio card indicator..RadioCardTitle - The title text inside a radio card (also inherits from .CardTitle).Note:
.RadioCardTitleelements also have the.CardTitleclass, so you can style all card titles together with.CardTitleand override specifically for radio cards with.RadioCardTitle.
The step indicator shows progress through multi-step forms.
.StepIndicatorContainer - The container wrapping all step indicators..StepIndicator - Individual step indicator item..StepIndicator--active - The currently active step..StepIndicator--completed - Steps that have been completed..StepIndicator--pending - Steps that are not yet reached..StepIndicatorLine - The connecting line between steps..StepIndicatorLine--top - The line segment above a step indicator..StepIndicatorLine--bottom - The line segment below a step indicator..StepIndicatorIcon - Icon displayed inside a step indicator (for completed steps)..StepIndicatorDot - The dot marker in a step indicator..StepIndicatorNumber - The step number displayed in the indicator..Loader - Loading indicators..ErrorMessage - Error message text..TooltipContent - Styles the content popover of a tooltip.Alert
.Alert - Alert boxes..Alert--error - Error alert variant..Alert--warning - Warning alert variant..Alert--info - Information alert variant..Alert--success - Success alert variant..AlertIcon - The icon inside an alert..AlertContent - The content/text area inside an alert.Chip
.Chip - Chips/Tags..Chip--info - Info chip variant..Chip--green - Green chip variant..Chip--red - Red chip variant..Chip--amber - Amber chip variant.The following rules are specific to the Consent Box widget and allow customization of consent-related UI elements:
.CategoryTag - Data use category identifier (e.g., "Compartir datos con terceros"). Appears above category descriptions in expanded consent views..Badge - Optional/required label badge appearing on consent items.Note: These consent-specific rules are only available in the
ConsentBoxwidget and will have no effect in other widgets likePrivacyCenterBox.
const appearance = {
theme: "soyio",
variables: {
fontFamily: "system-ui, sans-serif",
colorPrimary: "#f54c27",
colorSecondary: "#f54c27",
colorBackground: "#ffffff",
colorText: "#1E1B4B",
borderRadius: "0.5rem",
borderWidth: "3px",
borderStyle: "dashed",
},
rules: {
".MainContainer": {
borderWidth: "1px",
borderColor: "#E5E7EB",
borderRadius: "0.25rem",
},
".CheckboxInput": {
appearance: "none",
backgroundColor: "transparent",
cursor: "pointer",
width: "1rem",
height: "1rem",
borderRadius: "9999px",
borderWidth: "2px",
borderStyle: "solid",
borderColor: "var(--colorPrimary)",
},
".CheckboxInput--checked": {
borderColor: "var(--colorPrimary)",
backgroundColor: "var(--colorPrimary)",
},
".CheckboxInput:hover": {
borderColor: "var(--colorPrimary)",
boxShadow: "0 0 0 2px var(--colorPrimary)",
},
".CheckboxInput:focus": {
outline: "none",
boxShadow: "0 0 0 2px var(--colorPrimary)",
},
".CheckboxInput:focus-visible": {
outline: "none",
boxShadow: "0 0 0 2px var(--colorPrimary)",
},
".SwitchRoot": {
backgroundColor: "#e5e7eb",
borderRadius: "9999px",
boxShadow: "none", // Remove default shadow
},
".SwitchRoot--checked": {
backgroundColor: "var(--colorPrimary)",
},
".SwitchThumb": {
backgroundColor: "white",
borderRadius: "9999px",
boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
},
".SwitchIcon": {
display: "none", // Hide the check/cross icons
},
},
config: {
helperTextPosition: "top", // Position helper text above inputs
},
};
The SoyioTypes module from the @soyio/soyio-widget package provides TypeScript type definitions that you can use to integrate the SoyioWidget more seamlessly into your TypeScript projects.
To use the SoyioTypes in your project, import it directly from the @soyio/soyio-widget package:
import type { SoyioTypes } from "@soyio/soyio-widget";
The package includes JSON Schemas for validating configuration objects. This is useful for:
Validates appearance customization objects (theme, variables, rules, config):
// Import the schema
import appearanceSchema from "@soyio/soyio-widget/schemas/appearance";
// Use with a validator like Ajv
import Ajv from "ajv";
const ajv = new Ajv();
const validate = ajv.compile(appearanceSchema);
const appearance = {
theme: "soyio",
mode: "dark",
variables: {
colorPrimary: "#007bff",
borderRadius: "8px"
}
};
if (validate(appearance)) {
console.log("✅ Valid appearance config");
} else {
console.error("❌ Invalid:", validate.errors);
}
Validates complete widget configuration including Privacy Center and Consent Box options:
import configSchema from "@soyio/soyio-widget/schemas/config";
const validate = ajv.compile(configSchema);
const config = {
companyId: "com_example",
appearance: { /* ... */ },
onEvent: (event) => console.log(event)
};
if (validate(config)) {
const privacyCenter = new PrivacyCenterBox(config);
privacyCenter.mount("#container");
}
Perfect for building configuration editors:
import * as monaco from "monaco-editor";
import configSchema from "@soyio/soyio-widget/schemas/config";
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [{
uri: "https://soyio.id/widget-config.json",
fileMatch: ["*"],
schema: configSchema
}]
});
This package is not directly compatible with SSR because of post-robot, but there are workarounds that allow you to use it without disrupting the build process. Below are examples on popular SSR frameworks.
'use client'
import { useMemo, useEffect, useState } from "react";
export default function PrivacyCenterBox() {
const privacyCenterOptions = useMemo(() => ({
companyId: 'com_lalala',
// ... other options
onEvent: () => {}
}), []);
const [privacyCenter, setPrivacyCenter] = useState(null);
useEffect(() => {
async function mountPrivacyCenter() {
if (privacyCenter) return;
const { PrivacyCenterBox } = await import('@soyio/soyio-widget');
const privacyCenterBox = new PrivacyCenterBox(privacyCenterOptions);
privacyCenterBox.mount("#privacy-center-box");
setPrivacyCenter(privacyCenterBox);
}
mountPrivacyCenter();
return () => privacyCenter?.unmount();
}, [privacyCenterOptions, privacyCenter]);
return (
<div>
<h1>Hello World</h1>
<div id="privacy-center-box"></div>
</div>
);
}
// PrivacyCenterBox.tsx
import React, { useEffect, useState, useMemo } from "react";
export default function PrivacyCenterBox({ onReady }) {
const [privacyCenter, setPrivacyCenter] = useState(null);
const privacyCenterOptions = useMemo(() => ({
// other options...
companyId: process.env.SOYIO_COMPANY_ID || 'com_lalala',
onEvent: () => {}
}), [onReady]);
useEffect(() => {
async function mountPrivacyCenter() {
if (privacyCenter) return;
const { PrivacyCenterBox } = await import('@soyio/soyio-widget');
const privacyCenterBox = new PrivacyCenterBox(privacyCenterOptions);
privacyCenterBox.mount("#privacy-center-box");
setPrivacyCenter(privacyCenterBox);
}
mountPrivacyCenter();
return () => privacyCenter?.unmount();
}, [privacyCenterOptions, privacyCenter]);
return (<div id="privacy-center-box"></div>)
}
Then use React.lazy and React.Suspense when declaring that component elsewhere.
// AnotherComponent.tsx
import React, { useState, useCallback, useEffect } from "react";
const PrivacyCenterBox = React.lazy(() => import('yourPathTo/PrivacyCenterBox'));
export default function PrivacyCenterContainer() {
const [isClient, setIsClient] = useState(false);
useEffect(() => setIsClient(true), []);
const onReady = useCallback(() => setIsPrivacyCenterLoading(false), []);
return (
<div>
{isClient &&
<React.Suspense fallback={
<div>
Loading...
</div>
}>
<PrivacyCenterBox onReady={onReady} />
</React.Suspense>
}
</div>
)
}
To run the widget customization smoke test locally:
yarn run smoke
The smoke test configuration is loaded from smoke-test/.env.development. You can modify this file to change the default values:
VITE_COMPANY_ID=com_test
VITE_PRIVACY_CENTER_URL=http://localhost:5173
VITE_CONSENT_URL=http://localhost:5173
VITE_CONSENT_TEMPLATE_ID=constpl_test
To test DSR rights in the Privacy Center tab, add these parameters to the JSON config in the editor:
{
"enabledRights": ["arsop", "redec"],
"redecOperationIds": [
{ "id": "op_update", "label": "Update" },
{ "id": "op_rectification", "label": "Rectification" }
]
}
redecOperationIds is required when redec is included in enabledRights.
The smoke test includes preset management functionality that allows you to save, load, and share widget configurations:
All presets are automatically saved to your browser's localStorage. Use the export feature to persist presets to disk and share them with your team. See smoke-test/PRESETS.md for detailed documentation.
FAQs
Soyio Widget
The npm package @soyio/soyio-widget receives a total of 465 weekly downloads. As such, @soyio/soyio-widget popularity was classified as not popular.
We found that @soyio/soyio-widget demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.