@moodlehq/design-system 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/components/activity-icon/ActivityIcon.d.ts +23 -0
- package/dist/components/activity-icon/ActivityIcon.js +124 -0
- package/dist/components/activity-icon/ActivityIcon.js.map +1 -0
- package/dist/components/activity-icon/activityIconRegistry.d.ts +8 -0
- package/dist/components/activity-icon/activityIconRegistry.js +66 -0
- package/dist/components/activity-icon/activityIconRegistry.js.map +1 -0
- package/dist/components/activity-icon/assets/assignment.js +6 -0
- package/dist/components/activity-icon/assets/assignment.js.map +1 -0
- package/dist/components/activity-icon/assets/bigbluebutton.js +6 -0
- package/dist/components/activity-icon/assets/bigbluebutton.js.map +1 -0
- package/dist/components/activity-icon/assets/book.js +6 -0
- package/dist/components/activity-icon/assets/book.js.map +1 -0
- package/dist/components/activity-icon/assets/chat.js +6 -0
- package/dist/components/activity-icon/assets/chat.js.map +1 -0
- package/dist/components/activity-icon/assets/choice.js +6 -0
- package/dist/components/activity-icon/assets/choice.js.map +1 -0
- package/dist/components/activity-icon/assets/database.js +6 -0
- package/dist/components/activity-icon/assets/database.js.map +1 -0
- package/dist/components/activity-icon/assets/external-tool.js +6 -0
- package/dist/components/activity-icon/assets/external-tool.js.map +1 -0
- package/dist/components/activity-icon/assets/feedback.js +6 -0
- package/dist/components/activity-icon/assets/feedback.js.map +1 -0
- package/dist/components/activity-icon/assets/file-ai.js +6 -0
- package/dist/components/activity-icon/assets/file-ai.js.map +1 -0
- package/dist/components/activity-icon/assets/file-archive.js +6 -0
- package/dist/components/activity-icon/assets/file-archive.js.map +1 -0
- package/dist/components/activity-icon/assets/file-audio.js +6 -0
- package/dist/components/activity-icon/assets/file-audio.js.map +1 -0
- package/dist/components/activity-icon/assets/file-code.js +6 -0
- package/dist/components/activity-icon/assets/file-code.js.map +1 -0
- package/dist/components/activity-icon/assets/file-database.js +6 -0
- package/dist/components/activity-icon/assets/file-database.js.map +1 -0
- package/dist/components/activity-icon/assets/file-doc.js +6 -0
- package/dist/components/activity-icon/assets/file-doc.js.map +1 -0
- package/dist/components/activity-icon/assets/file-draw.js +6 -0
- package/dist/components/activity-icon/assets/file-draw.js.map +1 -0
- package/dist/components/activity-icon/assets/file-eps.js +6 -0
- package/dist/components/activity-icon/assets/file-eps.js.map +1 -0
- package/dist/components/activity-icon/assets/file-epub.js +6 -0
- package/dist/components/activity-icon/assets/file-epub.js.map +1 -0
- package/dist/components/activity-icon/assets/file-flash.js +6 -0
- package/dist/components/activity-icon/assets/file-flash.js.map +1 -0
- package/dist/components/activity-icon/assets/file-folder.js +6 -0
- package/dist/components/activity-icon/assets/file-folder.js.map +1 -0
- package/dist/components/activity-icon/assets/file-gif.js +6 -0
- package/dist/components/activity-icon/assets/file-gif.js.map +1 -0
- package/dist/components/activity-icon/assets/file-graphic.js +6 -0
- package/dist/components/activity-icon/assets/file-graphic.js.map +1 -0
- package/dist/components/activity-icon/assets/file-h5p.js +6 -0
- package/dist/components/activity-icon/assets/file-h5p.js.map +1 -0
- package/dist/components/activity-icon/assets/file-image.js +6 -0
- package/dist/components/activity-icon/assets/file-image.js.map +1 -0
- package/dist/components/activity-icon/assets/file-isf-flowchart.js +6 -0
- package/dist/components/activity-icon/assets/file-isf-flowchart.js.map +1 -0
- package/dist/components/activity-icon/assets/file-json.js +6 -0
- package/dist/components/activity-icon/assets/file-json.js.map +1 -0
- package/dist/components/activity-icon/assets/file-math.js +6 -0
- package/dist/components/activity-icon/assets/file-math.js.map +1 -0
- package/dist/components/activity-icon/assets/file-moodle.js +6 -0
- package/dist/components/activity-icon/assets/file-moodle.js.map +1 -0
- package/dist/components/activity-icon/assets/file-oth.js +6 -0
- package/dist/components/activity-icon/assets/file-oth.js.map +1 -0
- package/dist/components/activity-icon/assets/file-pdf.js +6 -0
- package/dist/components/activity-icon/assets/file-pdf.js.map +1 -0
- package/dist/components/activity-icon/assets/file-plain-text.js +6 -0
- package/dist/components/activity-icon/assets/file-plain-text.js.map +1 -0
- package/dist/components/activity-icon/assets/file-ppt.js +6 -0
- package/dist/components/activity-icon/assets/file-ppt.js.map +1 -0
- package/dist/components/activity-icon/assets/file-presentation.js +6 -0
- package/dist/components/activity-icon/assets/file-presentation.js.map +1 -0
- package/dist/components/activity-icon/assets/file-psd.js +6 -0
- package/dist/components/activity-icon/assets/file-psd.js.map +1 -0
- package/dist/components/activity-icon/assets/file-pub.js +6 -0
- package/dist/components/activity-icon/assets/file-pub.js.map +1 -0
- package/dist/components/activity-icon/assets/file-source-code.js +6 -0
- package/dist/components/activity-icon/assets/file-source-code.js.map +1 -0
- package/dist/components/activity-icon/assets/file-spreadsheet.js +6 -0
- package/dist/components/activity-icon/assets/file-spreadsheet.js.map +1 -0
- package/dist/components/activity-icon/assets/file-text-editor.js +6 -0
- package/dist/components/activity-icon/assets/file-text-editor.js.map +1 -0
- package/dist/components/activity-icon/assets/file-unknown.js +6 -0
- package/dist/components/activity-icon/assets/file-unknown.js.map +1 -0
- package/dist/components/activity-icon/assets/file-video.js +6 -0
- package/dist/components/activity-icon/assets/file-video.js.map +1 -0
- package/dist/components/activity-icon/assets/file-xls.js +6 -0
- package/dist/components/activity-icon/assets/file-xls.js.map +1 -0
- package/dist/components/activity-icon/assets/file.js +6 -0
- package/dist/components/activity-icon/assets/file.js.map +1 -0
- package/dist/components/activity-icon/assets/folder.js +6 -0
- package/dist/components/activity-icon/assets/folder.js.map +1 -0
- package/dist/components/activity-icon/assets/forum.js +6 -0
- package/dist/components/activity-icon/assets/forum.js.map +1 -0
- package/dist/components/activity-icon/assets/glossary.js +6 -0
- package/dist/components/activity-icon/assets/glossary.js.map +1 -0
- package/dist/components/activity-icon/assets/h5p.js +6 -0
- package/dist/components/activity-icon/assets/h5p.js.map +1 -0
- package/dist/components/activity-icon/assets/ims-package.js +6 -0
- package/dist/components/activity-icon/assets/ims-package.js.map +1 -0
- package/dist/components/activity-icon/assets/lesson.js +6 -0
- package/dist/components/activity-icon/assets/lesson.js.map +1 -0
- package/dist/components/activity-icon/assets/page.js +6 -0
- package/dist/components/activity-icon/assets/page.js.map +1 -0
- package/dist/components/activity-icon/assets/quiz.js +6 -0
- package/dist/components/activity-icon/assets/quiz.js.map +1 -0
- package/dist/components/activity-icon/assets/scorm-package.js +6 -0
- package/dist/components/activity-icon/assets/scorm-package.js.map +1 -0
- package/dist/components/activity-icon/assets/subsection.js +6 -0
- package/dist/components/activity-icon/assets/subsection.js.map +1 -0
- package/dist/components/activity-icon/assets/survey.js +6 -0
- package/dist/components/activity-icon/assets/survey.js.map +1 -0
- package/dist/components/activity-icon/assets/text-and-media.js +6 -0
- package/dist/components/activity-icon/assets/text-and-media.js.map +1 -0
- package/dist/components/activity-icon/assets/url.js +6 -0
- package/dist/components/activity-icon/assets/url.js.map +1 -0
- package/dist/components/activity-icon/assets/wiki.js +6 -0
- package/dist/components/activity-icon/assets/wiki.js.map +1 -0
- package/dist/components/activity-icon/assets/workshop.js +6 -0
- package/dist/components/activity-icon/assets/workshop.js.map +1 -0
- package/dist/components/activity-icon/index.d.ts +1 -0
- package/dist/components/activity-icon/index.js +2 -0
- package/dist/components/button/Button.js.map +1 -1
- package/dist/components/close-button/CloseButton.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/radio/Radio.js.map +1 -1
- package/dist/index.css +188 -1
- package/dist/index.js +2 -1
- package/package.json +4 -4
- package/tokens/css/colors.css +7 -0
- package/tokens/css/primitives.css +6 -0
- package/tokens/css/typography.css +1 -0
- package/tokens/scss/_colors.scss +7 -0
- package/tokens/scss/_primitives.scss +6 -0
- package/tokens/scss/_typography.scss +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","names":[],"sources":["../../../components/button/Button.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes, ReactElement } from 'react';\nimport { isValidElement } from 'react';\n\ntype ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'danger'\n | 'outline-primary'\n | 'outline-secondary'\n | 'outline-danger';\n\ntype IconElement = ReactElement<'i' | 'svg'>;\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n label?: string;\n variant?: ButtonVariant;\n size?: 'sm' | 'lg';\n startIcon?: IconElement;\n endIcon?: IconElement;\n}\n\n// Runtime guard — prop for icons must be <i> or <svg> elements\nconst isIconElement = (el: unknown, propName: string): el is IconElement => {\n const valid = isValidElement(el) && (el.type === 'i' || el.type === 'svg');\n if (!valid && el != null && import.meta.env.DEV) {\n console.error(`Button: \\`${propName}\\` must be an <i> or <svg> element.`);\n }\n return valid;\n};\n\nconst allowedVariants: ButtonVariant[] = [\n 'primary',\n 'secondary',\n 'danger',\n 'outline-primary',\n 'outline-secondary',\n 'outline-danger',\n];\n\nexport const Button = ({\n label,\n variant,\n size,\n startIcon,\n endIcon,\n className,\n type = 'button',\n ...props\n}: ButtonProps) => {\n // Warn in development if button has no accessible name\n if (import.meta.env.DEV) {\n const hasLabel = Boolean(label);\n const hasAriaLabel = 'aria-label' in props;\n if (!hasLabel && !hasAriaLabel) {\n console.warn(\n 'Button: label prop or aria-label attribute is required for accessibility.',\n );\n }\n if (variant && !allowedVariants.includes(variant as ButtonVariant)) {\n console.warn(\n `[MDS Button] Invalid variant \"${variant}\". Falling back to \"primary\". Allowed: ${allowedVariants.join(', ')}`,\n );\n }\n }\n\n const resolvedVariant =\n variant && allowedVariants.includes(variant as ButtonVariant)\n ? variant\n : 'primary';\n\n const classes = ['mds-btn', 'btn', `btn-${resolvedVariant}`];\n if (size) {\n classes.push(`btn-${size}`);\n }\n if (className) {\n classes.push(className);\n }\n\n return (\n <button className={classes.join(' ')} type={type} {...props}>\n {isIconElement(startIcon, 'startIcon') ? startIcon : null}\n {label}\n {isIconElement(endIcon, 'endIcon') ? endIcon : null}\n </button>\n );\n};\n"],"mappings":";;;AAsBA,IAAM,iBAAiB,IAAa,aAAwC;
|
|
1
|
+
{"version":3,"file":"Button.js","names":[],"sources":["../../../components/button/Button.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes, ReactElement } from 'react';\nimport { isValidElement } from 'react';\n\ntype ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'danger'\n | 'outline-primary'\n | 'outline-secondary'\n | 'outline-danger';\n\ntype IconElement = ReactElement<'i' | 'svg'>;\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n label?: string;\n variant?: ButtonVariant;\n size?: 'sm' | 'lg';\n startIcon?: IconElement;\n endIcon?: IconElement;\n}\n\n// Runtime guard — prop for icons must be <i> or <svg> elements\nconst isIconElement = (el: unknown, propName: string): el is IconElement => {\n const valid = isValidElement(el) && (el.type === 'i' || el.type === 'svg');\n if (!valid && el != null && import.meta.env.DEV) {\n console.error(`Button: \\`${propName}\\` must be an <i> or <svg> element.`);\n }\n return valid;\n};\n\nconst allowedVariants: ButtonVariant[] = [\n 'primary',\n 'secondary',\n 'danger',\n 'outline-primary',\n 'outline-secondary',\n 'outline-danger',\n];\n\nexport const Button = ({\n label,\n variant,\n size,\n startIcon,\n endIcon,\n className,\n type = 'button',\n ...props\n}: ButtonProps) => {\n // Warn in development if button has no accessible name\n if (import.meta.env.DEV) {\n const hasLabel = Boolean(label);\n const hasAriaLabel = 'aria-label' in props;\n if (!hasLabel && !hasAriaLabel) {\n console.warn(\n 'Button: label prop or aria-label attribute is required for accessibility.',\n );\n }\n if (variant && !allowedVariants.includes(variant as ButtonVariant)) {\n console.warn(\n `[MDS Button] Invalid variant \"${variant}\". Falling back to \"primary\". Allowed: ${allowedVariants.join(', ')}`,\n );\n }\n }\n\n const resolvedVariant =\n variant && allowedVariants.includes(variant as ButtonVariant)\n ? variant\n : 'primary';\n\n const classes = ['mds-btn', 'btn', `btn-${resolvedVariant}`];\n if (size) {\n classes.push(`btn-${size}`);\n }\n if (className) {\n classes.push(className);\n }\n\n return (\n <button className={classes.join(' ')} type={type} {...props}>\n {isIconElement(startIcon, 'startIcon') ? startIcon : null}\n {label}\n {isIconElement(endIcon, 'endIcon') ? endIcon : null}\n </button>\n );\n};\n"],"mappings":";;;AAsBA,IAAM,iBAAiB,IAAa,aAAwC;CAK1E,OAJc,eAAe,EAAE,MAAM,GAAG,SAAS,OAAO,GAAG,SAAS;AAKtE;AAEA,IAAM,kBAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAa,UAAU,EACrB,OACA,SACA,MACA,WACA,SACA,WACA,OAAO,UACP,GAAG,YACc;CAsBjB,MAAM,UAAU;EAAC;EAAW;EAAO,OAJjC,WAAW,gBAAgB,SAAS,OAAwB,IACxD,UACA;CAEqD;CAC3D,IAAI,MACF,QAAQ,KAAK,OAAO,MAAM;CAE5B,IAAI,WACF,QAAQ,KAAK,SAAS;CAGxB,OACE,qBAAC,UAAD;EAAQ,WAAW,QAAQ,KAAK,GAAG;EAAS;EAAM,GAAI;YAAtD;GACG,cAAc,WAAW,WAAW,IAAI,YAAY;GACpD;GACA,cAAc,SAAS,SAAS,IAAI,UAAU;EACzC;;AAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloseButton.js","names":[],"sources":["../../../components/close-button/CloseButton.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes } from 'react';\n\ntype CloseButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface CloseButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'type'\n> {\n /**\n * Accessible name announced by screen readers for the close button control.\n * Must be a translated string provided by the caller.\n */\n 'aria-label': string;\n\n /**\n * Visual size variant for the close icon button.\n * Invalid values fall back to the default `md` size at runtime.\n */\n size?: string;\n}\n\nconst allowedSizes: CloseButtonSize[] = ['sm', 'md', 'lg'];\n\nexport const CloseButton = ({\n 'aria-label': ariaLabel,\n size,\n className,\n ...props\n}: CloseButtonProps) => {\n if (\n import.meta.env.DEV &&\n size &&\n !allowedSizes.includes(size as CloseButtonSize)\n ) {\n console.warn(\n `[MDS CloseButton] Invalid size \"${size}\". Falling back to \"md\". Allowed: ${allowedSizes.join(', ')}`,\n );\n }\n\n const resolvedSize =\n size && allowedSizes.includes(size as CloseButtonSize) ? size : 'md';\n\n const classes = [\n 'mds-close-button',\n 'btn-close',\n `mds-close-button--${resolvedSize}`,\n ];\n if (className) {\n classes.push(className);\n }\n\n return (\n <button\n className={classes.join(' ')}\n aria-label={ariaLabel}\n {...props}\n type=\"button\"\n ></button>\n );\n};\n"],"mappings":";;AAqBA,IAAM,eAAkC;CAAC;CAAM;CAAM;
|
|
1
|
+
{"version":3,"file":"CloseButton.js","names":[],"sources":["../../../components/close-button/CloseButton.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes } from 'react';\n\ntype CloseButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface CloseButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'type'\n> {\n /**\n * Accessible name announced by screen readers for the close button control.\n * Must be a translated string provided by the caller.\n */\n 'aria-label': string;\n\n /**\n * Visual size variant for the close icon button.\n * Invalid values fall back to the default `md` size at runtime.\n */\n size?: string;\n}\n\nconst allowedSizes: CloseButtonSize[] = ['sm', 'md', 'lg'];\n\nexport const CloseButton = ({\n 'aria-label': ariaLabel,\n size,\n className,\n ...props\n}: CloseButtonProps) => {\n if (\n import.meta.env.DEV &&\n size &&\n !allowedSizes.includes(size as CloseButtonSize)\n ) {\n console.warn(\n `[MDS CloseButton] Invalid size \"${size}\". Falling back to \"md\". Allowed: ${allowedSizes.join(', ')}`,\n );\n }\n\n const resolvedSize =\n size && allowedSizes.includes(size as CloseButtonSize) ? size : 'md';\n\n const classes = [\n 'mds-close-button',\n 'btn-close',\n `mds-close-button--${resolvedSize}`,\n ];\n if (className) {\n classes.push(className);\n }\n\n return (\n <button\n className={classes.join(' ')}\n aria-label={ariaLabel}\n {...props}\n type=\"button\"\n ></button>\n );\n};\n"],"mappings":";;AAqBA,IAAM,eAAkC;CAAC;CAAM;CAAM;AAAI;AAEzD,IAAa,eAAe,EAC1B,cAAc,WACd,MACA,WACA,GAAG,YACmB;CActB,MAAM,UAAU;EACd;EACA;EACA,qBALA,QAAQ,aAAa,SAAS,IAAuB,IAAI,OAAO;CAMlE;CACA,IAAI,WACF,QAAQ,KAAK,SAAS;CAGxB,OACE,oBAAC,UAAD;EACE,WAAW,QAAQ,KAAK,GAAG;EAC3B,cAAY;EACZ,GAAI;EACJ,MAAK;CACE,CAAA;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Radio.js","names":[],"sources":["../../../components/radio/Radio.tsx"],"sourcesContent":["import { type InputHTMLAttributes, forwardRef, useId } from 'react';\n\n// Extend InputHTMLAttributes to allow passing any valid input attributes, but also add our custom props like feedback and label.\nexport interface RadioProps extends InputHTMLAttributes<HTMLInputElement> {\n /** Visible label text. When hideLabel is true this also serves as the aria-label fallback\n * if no explicit aria-label prop is provided. */\n label?: string;\n /** When true, the visible label element is hidden. The input is still labelled accessibly\n * via aria-label (prop) → label (prop) in that order of precedence. Suppresses\n * invalidFeedback — feedback text requires a visible label to provide context.\n *\n * Use cases: hideLabel is appropriate when the visual label would be redundant or visually cluttered,\n * such as in dense tables where the column header acts as the label, or in icon-only UIs. Always ensure\n * an accessible name is provided via aria-label or label prop. */\n hideLabel?: boolean;\n /** Marks the input as invalid: applies danger border/label colour and sets aria-invalid.\n * Independent of invalidFeedback — invalid styling can be shown without a message. */\n invalid?: boolean;\n /** Pre-translated error message rendered below the label. Requires invalid={true} and\n * hideLabel={false} to be displayed. Only invalid feedback is supported; is-valid\n * and neutral feedback states are intentionally not implemented. */\n invalidFeedback?: string;\n}\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(\n (\n {\n invalidFeedback,\n invalid,\n className,\n label,\n hideLabel = false,\n ...props\n }: RadioProps,\n ref,\n ) => {\n const generatedId = useId();\n const id = props.id ?? generatedId;\n\n // The invalid state on the input (border, aria-invalid) is independent of\n // hideLabel — a label-less input can still be invalid. Only the feedback text\n // requires a visible label for context and is suppressed when hideLabel is true.\n const isInvalid = !!invalid;\n\n if (import.meta.env.DEV) {\n if (hideLabel && !props['aria-label'] && !label) {\n console.warn(\n 'Radio: label prop or aria-label attribute is required for accessibility when hideLabel is true.',\n );\n }\n if (!hideLabel && !label) {\n console.warn(\n 'Radio: label prop is required when hideLabel is false. An empty label creates an inaccessible form control.',\n );\n }\n if (hideLabel && invalidFeedback) {\n console.warn(\n 'Radio: invalidFeedback is ignored when hideLabel is true. Feedback text requires a visible label to provide context.',\n );\n }\n if (!hideLabel && invalidFeedback && !invalid) {\n console.warn(\n 'Radio: invalidFeedback is provided without invalid={true}. Pass invalid={true} to apply invalid styling alongside the feedback text.',\n );\n }\n }\n // Build the class list for the radio wrapper div. mds-form-check is always applied\n // as a stable hook for consumers; form-check and layout classes are added only when\n // the label is visible (Bootstrap label/feedback styling and grid layout).\n const classes = ['mds-form-check'];\n if (!hideLabel) {\n classes.push('form-check');\n }\n if (className) {\n classes.push(className);\n }\n\n // When the label is hidden, derive aria-label from: caller's aria-label → label prop → undefined (warned above).\n const ariaLabel = hideLabel ? (props['aria-label'] ?? label) : undefined;\n\n // Link the input to its feedback text so screen readers announce the error message.\n // The ID is only generated when feedback will actually be rendered.\n const feedbackId =\n invalidFeedback && !hideLabel && invalid ? `${id}-feedback` : undefined;\n\n return (\n <div className={classes.join(' ')}>\n <input\n className={[\n 'mds-form-check-input',\n 'form-check-input',\n isInvalid ? 'is-invalid' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n type=\"radio\"\n ref={ref}\n {...props}\n aria-invalid={isInvalid ? true : undefined}\n aria-label={ariaLabel}\n aria-describedby={feedbackId}\n id={id} // Ensure we use the generated ID if no ID is provided, so the label can be properly associated with the input for accessibility.\n />\n {!hideLabel && (\n <label className=\"mds-form-check-label form-check-label\" htmlFor={id}>\n {label}\n </label>\n )}\n {feedbackId && (\n <div\n id={feedbackId}\n className=\"mds-form-check-feedback invalid-feedback\"\n >\n {invalidFeedback}\n </div>\n )}\n </div>\n );\n },\n);\nRadio.displayName = 'Radio';\n"],"mappings":";;;AAwBA,IAAa,QAAQ,YAEjB,EACE,iBACA,SACA,WACA,OACA,YAAY,OACZ,GAAG,SAEL,QACG;CACH,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"Radio.js","names":[],"sources":["../../../components/radio/Radio.tsx"],"sourcesContent":["import { type InputHTMLAttributes, forwardRef, useId } from 'react';\n\n// Extend InputHTMLAttributes to allow passing any valid input attributes, but also add our custom props like feedback and label.\nexport interface RadioProps extends InputHTMLAttributes<HTMLInputElement> {\n /** Visible label text. When hideLabel is true this also serves as the aria-label fallback\n * if no explicit aria-label prop is provided. */\n label?: string;\n /** When true, the visible label element is hidden. The input is still labelled accessibly\n * via aria-label (prop) → label (prop) in that order of precedence. Suppresses\n * invalidFeedback — feedback text requires a visible label to provide context.\n *\n * Use cases: hideLabel is appropriate when the visual label would be redundant or visually cluttered,\n * such as in dense tables where the column header acts as the label, or in icon-only UIs. Always ensure\n * an accessible name is provided via aria-label or label prop. */\n hideLabel?: boolean;\n /** Marks the input as invalid: applies danger border/label colour and sets aria-invalid.\n * Independent of invalidFeedback — invalid styling can be shown without a message. */\n invalid?: boolean;\n /** Pre-translated error message rendered below the label. Requires invalid={true} and\n * hideLabel={false} to be displayed. Only invalid feedback is supported; is-valid\n * and neutral feedback states are intentionally not implemented. */\n invalidFeedback?: string;\n}\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(\n (\n {\n invalidFeedback,\n invalid,\n className,\n label,\n hideLabel = false,\n ...props\n }: RadioProps,\n ref,\n ) => {\n const generatedId = useId();\n const id = props.id ?? generatedId;\n\n // The invalid state on the input (border, aria-invalid) is independent of\n // hideLabel — a label-less input can still be invalid. Only the feedback text\n // requires a visible label for context and is suppressed when hideLabel is true.\n const isInvalid = !!invalid;\n\n if (import.meta.env.DEV) {\n if (hideLabel && !props['aria-label'] && !label) {\n console.warn(\n 'Radio: label prop or aria-label attribute is required for accessibility when hideLabel is true.',\n );\n }\n if (!hideLabel && !label) {\n console.warn(\n 'Radio: label prop is required when hideLabel is false. An empty label creates an inaccessible form control.',\n );\n }\n if (hideLabel && invalidFeedback) {\n console.warn(\n 'Radio: invalidFeedback is ignored when hideLabel is true. Feedback text requires a visible label to provide context.',\n );\n }\n if (!hideLabel && invalidFeedback && !invalid) {\n console.warn(\n 'Radio: invalidFeedback is provided without invalid={true}. Pass invalid={true} to apply invalid styling alongside the feedback text.',\n );\n }\n }\n // Build the class list for the radio wrapper div. mds-form-check is always applied\n // as a stable hook for consumers; form-check and layout classes are added only when\n // the label is visible (Bootstrap label/feedback styling and grid layout).\n const classes = ['mds-form-check'];\n if (!hideLabel) {\n classes.push('form-check');\n }\n if (className) {\n classes.push(className);\n }\n\n // When the label is hidden, derive aria-label from: caller's aria-label → label prop → undefined (warned above).\n const ariaLabel = hideLabel ? (props['aria-label'] ?? label) : undefined;\n\n // Link the input to its feedback text so screen readers announce the error message.\n // The ID is only generated when feedback will actually be rendered.\n const feedbackId =\n invalidFeedback && !hideLabel && invalid ? `${id}-feedback` : undefined;\n\n return (\n <div className={classes.join(' ')}>\n <input\n className={[\n 'mds-form-check-input',\n 'form-check-input',\n isInvalid ? 'is-invalid' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n type=\"radio\"\n ref={ref}\n {...props}\n aria-invalid={isInvalid ? true : undefined}\n aria-label={ariaLabel}\n aria-describedby={feedbackId}\n id={id} // Ensure we use the generated ID if no ID is provided, so the label can be properly associated with the input for accessibility.\n />\n {!hideLabel && (\n <label className=\"mds-form-check-label form-check-label\" htmlFor={id}>\n {label}\n </label>\n )}\n {feedbackId && (\n <div\n id={feedbackId}\n className=\"mds-form-check-feedback invalid-feedback\"\n >\n {invalidFeedback}\n </div>\n )}\n </div>\n );\n },\n);\nRadio.displayName = 'Radio';\n"],"mappings":";;;AAwBA,IAAa,QAAQ,YAEjB,EACE,iBACA,SACA,WACA,OACA,YAAY,OACZ,GAAG,SAEL,QACG;CACH,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,MAAM,MAAM;CAKvB,MAAM,YAAY,CAAC,CAAC;CA2BpB,MAAM,UAAU,CAAC,gBAAgB;CACjC,IAAI,CAAC,WACH,QAAQ,KAAK,YAAY;CAE3B,IAAI,WACF,QAAQ,KAAK,SAAS;CAIxB,MAAM,YAAY,YAAa,MAAM,iBAAiB,QAAS,KAAA;CAI/D,MAAM,aACJ,mBAAmB,CAAC,aAAa,UAAU,GAAG,GAAG,aAAa,KAAA;CAEhE,OACE,qBAAC,OAAD;EAAK,WAAW,QAAQ,KAAK,GAAG;YAAhC;GACE,oBAAC,SAAD;IACE,WAAW;KACT;KACA;KACA,YAAY,eAAe;IAC7B,EACG,OAAO,OAAO,EACd,KAAK,GAAG;IACX,MAAK;IACA;IACL,GAAI;IACJ,gBAAc,YAAY,OAAO,KAAA;IACjC,cAAY;IACZ,oBAAkB;IACd;GACL,CAAA;GACA,CAAC,aACA,oBAAC,SAAD;IAAO,WAAU;IAAwC,SAAS;cAC/D;GACI,CAAA;GAER,cACC,oBAAC,OAAD;IACE,IAAI;IACJ,WAAU;cAET;GACE,CAAA;EAEJ;;AAET,CACF;AACA,MAAM,cAAc"}
|
package/dist/index.css
CHANGED
|
@@ -1,4 +1,109 @@
|
|
|
1
|
-
@charset "UTF-8";.mds-
|
|
1
|
+
@charset "UTF-8";.mds-activity-icon {
|
|
2
|
+
align-items: center;
|
|
3
|
+
/*
|
|
4
|
+
* content-box so block-size/inline-size declare the ICON content area.
|
|
5
|
+
* Padding then expands the tile beyond that — matching Figma's structure
|
|
6
|
+
* where "size" is the icon and padding is added by the container variant.
|
|
7
|
+
*/
|
|
8
|
+
box-sizing: content-box;
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
flex-shrink: 0;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* none: no background tile, no padding — tile equals the icon size */
|
|
16
|
+
|
|
17
|
+
.mds-activity-icon--none {
|
|
18
|
+
border-radius: var(--mds-border-radius-none);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* default: 8px padding each side — tile = icon + 16px (e.g. lg: 24+16 = 40px total).
|
|
23
|
+
* large: 12px padding each side — tile = icon + 24px (e.g. lg: 24+24 = 48px total).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
.mds-activity-icon--default {
|
|
27
|
+
border-radius: var(--mds-border-radius-xl);
|
|
28
|
+
padding: var(--mds-spacing-xs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.mds-activity-icon--large {
|
|
32
|
+
border-radius: var(--mds-border-radius-xl);
|
|
33
|
+
padding: var(--mds-spacing-sm);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Figma uses a smaller outer radius for default+sm only. */
|
|
37
|
+
|
|
38
|
+
.mds-activity-icon--default.mds-activity-icon--size-sm {
|
|
39
|
+
border-radius: var(--mds-border-radius-lg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Category background colors apply only when a background container is active. */
|
|
43
|
+
|
|
44
|
+
.mds-activity-icon--default.mds-activity-icon--category-assessment,
|
|
45
|
+
.mds-activity-icon--large.mds-activity-icon--category-assessment {
|
|
46
|
+
background-color: var(--mds-activity-icon-assessment-bg);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.mds-activity-icon--default.mds-activity-icon--category-collaboration,
|
|
50
|
+
.mds-activity-icon--large.mds-activity-icon--category-collaboration {
|
|
51
|
+
background-color: var(--mds-activity-icon-collaboration-bg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.mds-activity-icon--default.mds-activity-icon--category-communication,
|
|
55
|
+
.mds-activity-icon--large.mds-activity-icon--category-communication {
|
|
56
|
+
background-color: var(--mds-activity-icon-communication-bg);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.mds-activity-icon--default.mds-activity-icon--category-interactive,
|
|
60
|
+
.mds-activity-icon--large.mds-activity-icon--category-interactive {
|
|
61
|
+
background-color: var(--mds-activity-icon-interactive-bg);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.mds-activity-icon--default.mds-activity-icon--category-other,
|
|
65
|
+
.mds-activity-icon--large.mds-activity-icon--category-other {
|
|
66
|
+
background-color: var(--mds-activity-icon-other-bg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.mds-activity-icon--default.mds-activity-icon--category-resource,
|
|
70
|
+
.mds-activity-icon--large.mds-activity-icon--category-resource {
|
|
71
|
+
background-color: var(--mds-activity-icon-resource-bg);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Size variants */
|
|
75
|
+
|
|
76
|
+
.mds-activity-icon--size-sm {
|
|
77
|
+
block-size: var(--mds-icons-sm);
|
|
78
|
+
inline-size: var(--mds-icons-sm);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.mds-activity-icon--size-md {
|
|
82
|
+
/* Intentionally follows the Figma token mapping, where md uses the lg icon token. */
|
|
83
|
+
block-size: var(--mds-icons-lg);
|
|
84
|
+
inline-size: var(--mds-icons-lg);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.mds-activity-icon--size-lg {
|
|
88
|
+
/* Intentionally follows the Figma token mapping, where lg uses the xl icon token. */
|
|
89
|
+
block-size: var(--mds-icons-xl);
|
|
90
|
+
inline-size: var(--mds-icons-xl);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.mds-activity-icon--size-xl {
|
|
94
|
+
/* Intentionally follows the Figma token mapping, where xl uses the xxl icon token. */
|
|
95
|
+
block-size: var(--mds-icons-xxl);
|
|
96
|
+
inline-size: var(--mds-icons-xxl);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.mds-activity-icon__asset {
|
|
100
|
+
block-size: 100%;
|
|
101
|
+
border-radius: var(--mds-border-radius-xs);
|
|
102
|
+
display: block;
|
|
103
|
+
inline-size: 100%;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.mds-btn.btn {
|
|
2
107
|
background-color: var(--mds-bg-interactive-primary-default);
|
|
3
108
|
border: var(--mds-stroke-weight-sm) solid var(--mds-border-translucent);
|
|
4
109
|
border-radius: var(--mds-border-radius-md);
|
|
@@ -17,129 +122,161 @@
|
|
|
17
122
|
align-items: center;
|
|
18
123
|
gap: var(--mds-spacing-xxs);
|
|
19
124
|
}
|
|
125
|
+
|
|
20
126
|
.mds-btn.btn:hover {
|
|
21
127
|
background-color: var(--mds-bg-interactive-primary-hover);
|
|
22
128
|
}
|
|
129
|
+
|
|
23
130
|
.mds-btn.btn:active {
|
|
24
131
|
background-color: var(--mds-bg-interactive-primary-active);
|
|
25
132
|
border: var(--mds-stroke-weight-sm) solid var(--mds-border-translucent);
|
|
26
133
|
}
|
|
134
|
+
|
|
27
135
|
.mds-btn.btn:disabled {
|
|
28
136
|
background-color: var(--mds-bg-interactive-primary-disabled);
|
|
29
137
|
}
|
|
138
|
+
|
|
30
139
|
/**
|
|
31
140
|
* Secondary variants
|
|
32
141
|
*/
|
|
142
|
+
|
|
33
143
|
.mds-btn.btn.btn-secondary {
|
|
34
144
|
background-color: var(--mds-bg-interactive-secondary-default);
|
|
35
145
|
color: var(--mds-text-default);
|
|
36
146
|
}
|
|
147
|
+
|
|
37
148
|
.mds-btn.btn.btn-secondary:hover {
|
|
38
149
|
background-color: var(--mds-bg-interactive-secondary-hover);
|
|
39
150
|
}
|
|
151
|
+
|
|
40
152
|
.mds-btn.btn.btn-secondary:active {
|
|
41
153
|
background-color: var(--mds-bg-interactive-secondary-active);
|
|
42
154
|
}
|
|
155
|
+
|
|
43
156
|
.mds-btn.btn.btn-secondary:disabled {
|
|
44
157
|
background-color: var(--mds-bg-interactive-secondary-disabled);
|
|
45
158
|
}
|
|
159
|
+
|
|
46
160
|
/**
|
|
47
161
|
* Danger variants
|
|
48
162
|
*/
|
|
163
|
+
|
|
49
164
|
.mds-btn.btn-danger {
|
|
50
165
|
background-color: var(--mds-bg-interactive-danger-default);
|
|
51
166
|
}
|
|
167
|
+
|
|
52
168
|
.mds-btn.btn-danger:hover {
|
|
53
169
|
background-color: var(--mds-bg-interactive-danger-hover);
|
|
54
170
|
}
|
|
171
|
+
|
|
55
172
|
.mds-btn.btn-danger:active {
|
|
56
173
|
background-color: var(--mds-bg-interactive-danger-active);
|
|
57
174
|
}
|
|
175
|
+
|
|
58
176
|
.mds-btn.btn.btn-danger:disabled {
|
|
59
177
|
background-color: var(--mds-bg-interactive-danger-disabled);
|
|
60
178
|
}
|
|
179
|
+
|
|
61
180
|
/**
|
|
62
181
|
* Outline Primary variants
|
|
63
182
|
*/
|
|
183
|
+
|
|
64
184
|
.mds-btn.btn-outline-primary {
|
|
65
185
|
background-color: transparent;
|
|
66
186
|
border: var(--mds-stroke-weight-sm) solid
|
|
67
187
|
var(--mds-border-interactive-primary-default);
|
|
68
188
|
color: var(--mds-text-link-primary-default);
|
|
69
189
|
}
|
|
190
|
+
|
|
70
191
|
.mds-btn.btn-outline-primary:hover {
|
|
71
192
|
background-color: var(--mds-bg-interactive-primary-hover);
|
|
72
193
|
color: var(--mds-text-inverse);
|
|
73
194
|
}
|
|
195
|
+
|
|
74
196
|
.mds-btn.btn-outline-primary:active {
|
|
75
197
|
background-color: var(--mds-bg-interactive-primary-active);
|
|
76
198
|
color: var(--mds-text-inverse);
|
|
77
199
|
}
|
|
200
|
+
|
|
78
201
|
.mds-btn.btn.btn-outline-primary:disabled {
|
|
79
202
|
background-color: transparent;
|
|
80
203
|
border: var(--mds-stroke-weight-sm) solid
|
|
81
204
|
var(--mds-border-interactive-primary-disabled);
|
|
82
205
|
color: var(--mds-color-blue-300);
|
|
83
206
|
}
|
|
207
|
+
|
|
84
208
|
/**
|
|
85
209
|
* Outline Secondary variants
|
|
86
210
|
*/
|
|
211
|
+
|
|
87
212
|
.mds-btn.btn-outline-secondary {
|
|
88
213
|
background-color: transparent;
|
|
89
214
|
border: var(--mds-stroke-weight-sm) solid
|
|
90
215
|
var(--mds-border-interactive-secondary-default);
|
|
91
216
|
color: var(--mds-text-muted);
|
|
92
217
|
}
|
|
218
|
+
|
|
93
219
|
.mds-btn.btn-outline-secondary:hover {
|
|
94
220
|
background-color: var(--mds-border-interactive-secondary-hover);
|
|
95
221
|
color: var(--mds-text-inverse);
|
|
96
222
|
}
|
|
223
|
+
|
|
97
224
|
.mds-btn.btn-outline-secondary:active {
|
|
98
225
|
background-color: var(--mds-border-interactive-secondary-active);
|
|
99
226
|
color: var(--mds-text-inverse);
|
|
100
227
|
}
|
|
228
|
+
|
|
101
229
|
.mds-btn.btn.btn-outline-secondary:disabled {
|
|
102
230
|
background-color: transparent;
|
|
103
231
|
border: var(--mds-stroke-weight-sm) solid
|
|
104
232
|
var(--mds-border-interactive-secondary-disabled);
|
|
105
233
|
color: var(--mds-color-gray-500);
|
|
106
234
|
}
|
|
235
|
+
|
|
107
236
|
/**
|
|
108
237
|
* Outline Danger variants
|
|
109
238
|
*/
|
|
239
|
+
|
|
110
240
|
.mds-btn.btn-outline-danger {
|
|
111
241
|
background-color: transparent;
|
|
112
242
|
border: var(--mds-stroke-weight-sm) solid
|
|
113
243
|
var(--mds-border-interactive-danger-default);
|
|
114
244
|
color: var(--mds-text-danger);
|
|
115
245
|
}
|
|
246
|
+
|
|
116
247
|
.mds-btn.btn-outline-danger:hover {
|
|
117
248
|
background-color: var(--mds-bg-interactive-danger-default);
|
|
118
249
|
color: var(--mds-text-inverse);
|
|
119
250
|
}
|
|
251
|
+
|
|
120
252
|
.mds-btn.btn-outline-danger:active {
|
|
121
253
|
background-color: var(--mds-bg-interactive-danger-hover);
|
|
122
254
|
color: var(--mds-text-inverse);
|
|
123
255
|
}
|
|
256
|
+
|
|
124
257
|
.mds-btn.btn.btn-outline-danger:disabled {
|
|
125
258
|
background-color: transparent;
|
|
126
259
|
border: var(--mds-stroke-weight-sm) solid
|
|
127
260
|
var(--mds-border-interactive-danger-disabled);
|
|
128
261
|
color: var(--mds-color-red-300);
|
|
129
262
|
}
|
|
263
|
+
|
|
130
264
|
/**
|
|
131
265
|
* Size variants
|
|
132
266
|
*/
|
|
267
|
+
|
|
133
268
|
.mds-btn.btn-sm {
|
|
134
269
|
padding: var(--mds-spacing-xxs) var(--mds-spacing-xs);
|
|
135
270
|
font-size: var(--mds-font-size-paragraph-small);
|
|
136
271
|
line-height: var(--mds-line-height-paragraph-small);
|
|
137
272
|
}
|
|
273
|
+
|
|
138
274
|
.mds-btn.btn-lg {
|
|
139
275
|
padding: var(--mds-spacing-xs) var(--mds-spacing-md);
|
|
140
276
|
font-size: var(--mds-font-size-paragraph-lead);
|
|
141
277
|
line-height: var(--mds-line-height-paragraph-default);
|
|
142
278
|
}
|
|
279
|
+
|
|
143
280
|
.mds-close-button.btn-close {
|
|
144
281
|
inline-size: var(--mds-icons-md);
|
|
145
282
|
block-size: var(--mds-icons-md);
|
|
@@ -149,38 +286,47 @@
|
|
|
149
286
|
background-size: var(--mds-icons-md) var(--mds-icons-md);
|
|
150
287
|
opacity: 0.6;
|
|
151
288
|
}
|
|
289
|
+
|
|
152
290
|
.mds-close-button.btn-close:hover {
|
|
153
291
|
opacity: 0.75;
|
|
154
292
|
}
|
|
293
|
+
|
|
155
294
|
.mds-close-button.btn-close:focus {
|
|
156
295
|
box-shadow: none;
|
|
157
296
|
}
|
|
297
|
+
|
|
158
298
|
.mds-close-button.btn-close:focus-visible {
|
|
159
299
|
outline: var(--mds-stroke-weight-md) solid var(--mds-focus-default);
|
|
160
300
|
outline-offset: 0;
|
|
161
301
|
box-shadow: none;
|
|
162
302
|
opacity: 1;
|
|
163
303
|
}
|
|
304
|
+
|
|
164
305
|
.mds-close-button.btn-close:disabled,
|
|
165
306
|
.mds-close-button.btn-close.disabled {
|
|
166
307
|
opacity: 0.25;
|
|
167
308
|
}
|
|
309
|
+
|
|
168
310
|
.mds-close-button.btn-close.mds-close-button--sm {
|
|
169
311
|
inline-size: var(--mds-icons-sm);
|
|
170
312
|
block-size: var(--mds-icons-sm);
|
|
171
313
|
background-size: var(--mds-icons-sm) var(--mds-icons-sm);
|
|
172
314
|
}
|
|
315
|
+
|
|
173
316
|
.mds-close-button.btn-close.mds-close-button--md {
|
|
174
317
|
inline-size: var(--mds-icons-md);
|
|
175
318
|
block-size: var(--mds-icons-md);
|
|
176
319
|
background-size: var(--mds-icons-md) var(--mds-icons-md);
|
|
177
320
|
}
|
|
321
|
+
|
|
178
322
|
.mds-close-button.btn-close.mds-close-button--lg {
|
|
179
323
|
inline-size: var(--mds-icons-lg);
|
|
180
324
|
block-size: var(--mds-icons-lg);
|
|
181
325
|
background-size: var(--mds-icons-lg) var(--mds-icons-lg);
|
|
182
326
|
}
|
|
327
|
+
|
|
183
328
|
/* Parent element */
|
|
329
|
+
|
|
184
330
|
.mds-form-check {
|
|
185
331
|
/* Grid layout: column 1 = input, column 2 = label. The feedback is then placed
|
|
186
332
|
explicitly into column 2 via grid-column so it aligns with the label without
|
|
@@ -210,7 +356,9 @@
|
|
|
210
356
|
line-height: var(--mds-line-height-paragraph-small, 1.0875rem); /* 108.75% */
|
|
211
357
|
letter-spacing: var(--mds-letter-spacing, 0);
|
|
212
358
|
}
|
|
359
|
+
|
|
213
360
|
/* Input element styles */
|
|
361
|
+
|
|
214
362
|
.mds-form-check-input {
|
|
215
363
|
border-radius: var(--mds-border-radius-pill, 50rem);
|
|
216
364
|
border: var(--mds-stroke-weight-sm, 1px) solid
|
|
@@ -219,83 +367,106 @@
|
|
|
219
367
|
background-image to none, which wipes out Bootstrap's --bs-form-check-bg-image
|
|
220
368
|
(the inner dot SVG) on the checked state. */
|
|
221
369
|
}
|
|
370
|
+
|
|
222
371
|
/* Bootstrap's .form-check .form-check-input rule (specificity 0,2,0) sets float: left
|
|
223
372
|
and a negative margin-inline-start that causes overlap. Match its specificity to
|
|
224
373
|
ensure our flex-friendly reset wins regardless of load order. */
|
|
374
|
+
|
|
225
375
|
.mds-form-check .mds-form-check-input {
|
|
226
376
|
margin: 0;
|
|
227
377
|
float: none;
|
|
228
378
|
background-color: var(--mds-bg-surface-default, #fff);
|
|
229
379
|
}
|
|
380
|
+
|
|
230
381
|
.mds-form-check-input:checked {
|
|
231
382
|
background-color: var(--mds-bg-interactive-primary-default);
|
|
232
383
|
border-color: transparent;
|
|
233
384
|
}
|
|
385
|
+
|
|
234
386
|
.mds-form-check-input:disabled {
|
|
235
387
|
border-color: var(--mds-border-interactive-secondary-disabled);
|
|
236
388
|
}
|
|
389
|
+
|
|
237
390
|
.mds-form-check-input:disabled:checked {
|
|
238
391
|
background-color: var(--mds-bg-interactive-primary-disabled);
|
|
239
392
|
border-color: transparent;
|
|
240
393
|
}
|
|
394
|
+
|
|
241
395
|
.mds-form-check-input.is-invalid {
|
|
242
396
|
border-color: var(--mds-border-interactive-danger-default);
|
|
243
397
|
}
|
|
398
|
+
|
|
244
399
|
.mds-form-check-input.is-invalid:checked {
|
|
245
400
|
background-color: var(--mds-bg-interactive-danger-default);
|
|
246
401
|
border-color: transparent;
|
|
247
402
|
}
|
|
403
|
+
|
|
248
404
|
/* Explicit rule for invalid + disabled to avoid relying on specificity cascade order.
|
|
249
405
|
Disabled takes visual precedence: use the disabled background/border, not the danger ones. */
|
|
406
|
+
|
|
250
407
|
.mds-form-check-input.is-invalid:disabled {
|
|
251
408
|
background-color: var(--mds-bg-interactive-secondary-disabled);
|
|
252
409
|
border-color: var(--mds-border-interactive-secondary-disabled);
|
|
253
410
|
}
|
|
411
|
+
|
|
254
412
|
.mds-form-check-input.is-invalid:disabled:checked {
|
|
255
413
|
background-color: var(--mds-bg-interactive-primary-disabled);
|
|
256
414
|
border-color: transparent;
|
|
257
415
|
}
|
|
416
|
+
|
|
258
417
|
/* is-valid (Bootstrap) and neutral feedback states are intentionally not supported.
|
|
259
418
|
Radio buttons surface a binary valid/invalid result; positive confirmation
|
|
260
419
|
is conveyed by form-level success messaging rather than per-input valid styling. */
|
|
420
|
+
|
|
261
421
|
/* Hover styles are intentionally omitted. The radio input relies on the OS/browser
|
|
262
422
|
default appearance for hover; no design token exists for a radio hover state. */
|
|
423
|
+
|
|
263
424
|
/* Focus ring: Only outline + outline-offset allowed (no box-shadow or border).
|
|
264
425
|
See .github/instructions/components.instructions.md for full rules and rationale. */
|
|
426
|
+
|
|
265
427
|
.mds-form-check-input:focus,
|
|
266
428
|
.mds-form-check-input.is-invalid:focus {
|
|
267
429
|
/* Reset Bootstrap's :focus box-shadow; our ring is applied on :focus-visible only */
|
|
268
430
|
box-shadow: none;
|
|
269
431
|
outline: none;
|
|
270
432
|
}
|
|
433
|
+
|
|
271
434
|
.mds-form-check-input:focus-visible {
|
|
272
435
|
outline: var(--mds-stroke-weight-md) solid var(--mds-focus-default);
|
|
273
436
|
outline-offset: var(--mds-spacing-offset);
|
|
274
437
|
box-shadow: none;
|
|
275
438
|
}
|
|
439
|
+
|
|
276
440
|
.mds-form-check-input.is-invalid:focus-visible {
|
|
277
441
|
outline: var(--mds-stroke-weight-md) solid var(--mds-border-feedback-danger);
|
|
278
442
|
outline-offset: var(--mds-spacing-offset);
|
|
279
443
|
box-shadow: none;
|
|
280
444
|
}
|
|
445
|
+
|
|
281
446
|
/* Label element styles */
|
|
447
|
+
|
|
282
448
|
.mds-form-check .form-check-label,
|
|
283
449
|
.mds-form-check .mds-form-check-label {
|
|
284
450
|
color: var(--mds-text-default, #1d2125);
|
|
285
451
|
cursor: pointer;
|
|
286
452
|
}
|
|
453
|
+
|
|
287
454
|
/* Use sibling selector so the label reflects the input's disabled/invalid state */
|
|
455
|
+
|
|
288
456
|
.mds-form-check .mds-form-check-input:disabled ~ .form-check-label,
|
|
289
457
|
.mds-form-check .mds-form-check-input:disabled ~ .mds-form-check-label {
|
|
290
458
|
color: var(--mds-text-muted);
|
|
291
459
|
/* Disabled inputs are not interactive — suppress the pointer cursor on the label */
|
|
292
460
|
cursor: default;
|
|
293
461
|
}
|
|
462
|
+
|
|
294
463
|
.mds-form-check .mds-form-check-input.is-invalid ~ .form-check-label,
|
|
295
464
|
.mds-form-check .mds-form-check-input.is-invalid ~ .mds-form-check-label {
|
|
296
465
|
color: var(--mds-text-danger);
|
|
297
466
|
}
|
|
467
|
+
|
|
298
468
|
/* Feedback element styles — UI text/UI small */
|
|
469
|
+
|
|
299
470
|
.mds-form-check .invalid-feedback,
|
|
300
471
|
.mds-form-check .mds-form-check-feedback {
|
|
301
472
|
/* Place feedback in column 2 (the label column) so it aligns with the label
|
|
@@ -305,8 +476,10 @@
|
|
|
305
476
|
font-weight: var(--mds-font-weight-medium, 500);
|
|
306
477
|
color: var(--mds-text-danger);
|
|
307
478
|
}
|
|
479
|
+
|
|
308
480
|
/* Dim the feedback when the input is disabled to match the reduced visual weight of
|
|
309
481
|
the label and input; keeps the feedback legible without implying it is actionable. */
|
|
482
|
+
|
|
310
483
|
.mds-form-check:has(.mds-form-check-input:disabled) .invalid-feedback,
|
|
311
484
|
.mds-form-check:has(.mds-form-check-input:disabled) .mds-form-check-feedback {
|
|
312
485
|
opacity: 0.5;
|
|
@@ -372,23 +545,30 @@
|
|
|
372
545
|
--mds-activity-icon-other-icon: var(--mds-color-gray-900);
|
|
373
546
|
--mds-activity-icon-resource-bg: var(--mds-color-cyan-100);
|
|
374
547
|
--mds-activity-icon-resource-icon: var(--mds-color-cyan-500);
|
|
548
|
+
--mds-bg-feedback-danger-light: var(--mds-color-red-50);
|
|
375
549
|
--mds-bg-feedback-danger-subtle: var(--mds-color-red-100);
|
|
376
550
|
--mds-bg-feedback-info-default: var(--mds-color-cyan-500);
|
|
551
|
+
--mds-bg-feedback-info-light: var(--mds-color-cyan-50);
|
|
377
552
|
--mds-bg-feedback-info-subtle: var(--mds-color-cyan-100);
|
|
378
553
|
--mds-bg-feedback-primary-default: var(--mds-color-blue-500);
|
|
554
|
+
--mds-bg-feedback-primary-light: var(--mds-color-blue-50);
|
|
379
555
|
--mds-bg-feedback-primary-subtle: var(--mds-color-blue-100);
|
|
380
556
|
--mds-bg-feedback-secondary-default: var(--mds-color-gray-400);
|
|
381
557
|
--mds-bg-feedback-secondary-subtle: var(--mds-color-gray-100);
|
|
382
558
|
--mds-bg-feedback-success-default: var(--mds-color-green-500);
|
|
559
|
+
--mds-bg-feedback-success-light: var(--mds-color-green-50);
|
|
383
560
|
--mds-bg-feedback-success-subtle: var(--mds-color-green-100);
|
|
384
561
|
--mds-bg-feedback-warning-default: var(--mds-color-yellow-500);
|
|
562
|
+
--mds-bg-feedback-warning-light: var(--mds-color-yellow-50);
|
|
385
563
|
--mds-bg-feedback-warning-subtle: var(--mds-color-yellow-100);
|
|
386
564
|
--mds-bg-interactive-danger-active: var(--mds-color-red-700);
|
|
387
565
|
--mds-bg-interactive-danger-default: var(--mds-color-red-500);
|
|
566
|
+
--mds-bg-interactive-danger-default-light: var(--mds-color-red-50);
|
|
388
567
|
--mds-bg-interactive-danger-disabled: var(--mds-color-red-200);
|
|
389
568
|
--mds-bg-interactive-danger-hover: var(--mds-color-red-600);
|
|
390
569
|
--mds-bg-interactive-primary-active: var(--mds-color-blue-700);
|
|
391
570
|
--mds-bg-interactive-primary-default: var(--mds-color-blue-500);
|
|
571
|
+
--mds-bg-interactive-primary-default-light: var(--mds-color-blue-50);
|
|
392
572
|
--mds-bg-interactive-primary-disabled: var(--mds-color-blue-200);
|
|
393
573
|
--mds-bg-interactive-primary-hover: var(--mds-color-blue-600);
|
|
394
574
|
--mds-bg-interactive-secondary-default: var(--mds-color-gray-400);
|
|
@@ -444,6 +624,7 @@
|
|
|
444
624
|
*/
|
|
445
625
|
|
|
446
626
|
:root {
|
|
627
|
+
--mds-color-blue-50: #e7f0f9;
|
|
447
628
|
--mds-color-blue-100: #cfe2f2;
|
|
448
629
|
--mds-color-blue-200: #9fc4e5;
|
|
449
630
|
--mds-color-blue-300: #6fa7d9;
|
|
@@ -453,6 +634,7 @@
|
|
|
453
634
|
--mds-color-blue-700: #094173;
|
|
454
635
|
--mds-color-blue-800: #062b4c;
|
|
455
636
|
--mds-color-blue-900: #031626;
|
|
637
|
+
--mds-color-cyan-50: #e5f2f4;
|
|
456
638
|
--mds-color-cyan-100: #cce6ea;
|
|
457
639
|
--mds-color-cyan-200: #99cdd5;
|
|
458
640
|
--mds-color-cyan-300: #66b3c0;
|
|
@@ -473,6 +655,7 @@
|
|
|
473
655
|
--mds-color-gray-900: #1d2125;
|
|
474
656
|
--mds-color-gray-black: #000000;
|
|
475
657
|
--mds-color-gray-white: #ffffff;
|
|
658
|
+
--mds-color-green-50: #ebf2ea;
|
|
476
659
|
--mds-color-green-100: #d7e4d6;
|
|
477
660
|
--mds-color-green-200: #aecaad;
|
|
478
661
|
--mds-color-green-300: #86af84;
|
|
@@ -518,6 +701,7 @@
|
|
|
518
701
|
--mds-color-purple-700: #3a254a;
|
|
519
702
|
--mds-color-purple-800: #271832;
|
|
520
703
|
--mds-color-purple-900: #130c19;
|
|
704
|
+
--mds-color-red-50: #faeae9;
|
|
521
705
|
--mds-color-red-100: #f4d6d2;
|
|
522
706
|
--mds-color-red-200: #eaada6;
|
|
523
707
|
--mds-color-red-300: #df8379;
|
|
@@ -536,6 +720,7 @@
|
|
|
536
720
|
--mds-color-teal-700: #13795b;
|
|
537
721
|
--mds-color-teal-800: #0d503c;
|
|
538
722
|
--mds-color-teal-900: #06281e;
|
|
723
|
+
--mds-color-yellow-50: #fdf7ed;
|
|
539
724
|
--mds-color-yellow-100: #fcefdc;
|
|
540
725
|
--mds-color-yellow-200: #f9deb8;
|
|
541
726
|
--mds-color-yellow-300: #f6ce95;
|
|
@@ -600,6 +785,7 @@
|
|
|
600
785
|
--mds-typography-line-height-paragraph-base: 1.5rem;
|
|
601
786
|
--mds-typography-line-height-paragraph-lg: 2.5rem;
|
|
602
787
|
--mds-typography-line-height-paragraph-sm: 1.0875rem;
|
|
788
|
+
--mds-typography-line-height-paragraph-xs: 0.75rem;
|
|
603
789
|
}
|
|
604
790
|
|
|
605
791
|
/**
|
|
@@ -692,6 +878,7 @@
|
|
|
692
878
|
--mds-line-height-paragraph-default: var(--mds-typography-line-height-paragraph-base); /** Comfortable line height for paragraphs. */
|
|
693
879
|
--mds-line-height-paragraph-lead: var(--mds-typography-line-height-paragraph-lg); /** Extended line height for large body text. */
|
|
694
880
|
--mds-line-height-paragraph-small: var(--mds-typography-line-height-paragraph-sm); /** Compact line height for small text. */
|
|
881
|
+
--mds-line-height-paragraph-xs: var(--mds-typography-line-height-paragraph-xs);
|
|
695
882
|
--mds-margin-bottom-display: var(--mds-spacing-none); /** Default margin below a Displays. */
|
|
696
883
|
--mds-margin-bottom-heading: var(--mds-spacing-xs); /** Default margin below a Headings. */
|
|
697
884
|
--mds-margin-bottom-paragraph: var(--mds-spacing-md); /** Default margin below a paragraph. */
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* empty css */
|
|
2
2
|
/* empty css */
|
|
3
3
|
/* empty css */
|
|
4
|
+
import { ActivityIcon } from "./components/activity-icon/ActivityIcon.js";
|
|
4
5
|
import { Button } from "./components/button/Button.js";
|
|
5
6
|
import { CloseButton } from "./components/close-button/CloseButton.js";
|
|
6
7
|
import { Radio } from "./components/radio/Radio.js";
|
|
7
|
-
export { Button, CloseButton, Radio };
|
|
8
|
+
export { ActivityIcon, Button, CloseButton, Radio };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moodlehq/design-system",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "The Moodle Design System",
|
|
5
5
|
"files": [
|
|
6
6
|
"/dist",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@chromatic-com/storybook": "^5.0.0",
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
48
|
-
"@commitlint/cli": "^
|
|
48
|
+
"@commitlint/cli": "^21.0.1",
|
|
49
49
|
"@commitlint/config-conventional": "^20.0.0",
|
|
50
50
|
"@eslint/compat": "^2.0.0",
|
|
51
51
|
"@eslint/eslintrc": "^3.3.3",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"globals": "^17.1.0",
|
|
83
83
|
"husky": "^9.1.7",
|
|
84
84
|
"jsdom": "^29.0.0",
|
|
85
|
-
"lint-staged": "^
|
|
85
|
+
"lint-staged": "^17.0.5",
|
|
86
86
|
"playwright": "^1.57.0",
|
|
87
87
|
"prettier": "^3.7.3",
|
|
88
88
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"typescript": "^5.9.3",
|
|
98
98
|
"typescript-eslint": "^8.57.1",
|
|
99
99
|
"vite": "^8.0.0",
|
|
100
|
-
"vite-plugin-dts": "^
|
|
100
|
+
"vite-plugin-dts": "^5.0.0",
|
|
101
101
|
"vite-tsconfig-paths": "^6.0.1",
|
|
102
102
|
"vitest": "^4.0.4"
|
|
103
103
|
},
|
package/tokens/css/colors.css
CHANGED
|
@@ -18,23 +18,30 @@
|
|
|
18
18
|
--mds-activity-icon-other-icon: var(--mds-color-gray-900);
|
|
19
19
|
--mds-activity-icon-resource-bg: var(--mds-color-cyan-100);
|
|
20
20
|
--mds-activity-icon-resource-icon: var(--mds-color-cyan-500);
|
|
21
|
+
--mds-bg-feedback-danger-light: var(--mds-color-red-50);
|
|
21
22
|
--mds-bg-feedback-danger-subtle: var(--mds-color-red-100);
|
|
22
23
|
--mds-bg-feedback-info-default: var(--mds-color-cyan-500);
|
|
24
|
+
--mds-bg-feedback-info-light: var(--mds-color-cyan-50);
|
|
23
25
|
--mds-bg-feedback-info-subtle: var(--mds-color-cyan-100);
|
|
24
26
|
--mds-bg-feedback-primary-default: var(--mds-color-blue-500);
|
|
27
|
+
--mds-bg-feedback-primary-light: var(--mds-color-blue-50);
|
|
25
28
|
--mds-bg-feedback-primary-subtle: var(--mds-color-blue-100);
|
|
26
29
|
--mds-bg-feedback-secondary-default: var(--mds-color-gray-400);
|
|
27
30
|
--mds-bg-feedback-secondary-subtle: var(--mds-color-gray-100);
|
|
28
31
|
--mds-bg-feedback-success-default: var(--mds-color-green-500);
|
|
32
|
+
--mds-bg-feedback-success-light: var(--mds-color-green-50);
|
|
29
33
|
--mds-bg-feedback-success-subtle: var(--mds-color-green-100);
|
|
30
34
|
--mds-bg-feedback-warning-default: var(--mds-color-yellow-500);
|
|
35
|
+
--mds-bg-feedback-warning-light: var(--mds-color-yellow-50);
|
|
31
36
|
--mds-bg-feedback-warning-subtle: var(--mds-color-yellow-100);
|
|
32
37
|
--mds-bg-interactive-danger-active: var(--mds-color-red-700);
|
|
33
38
|
--mds-bg-interactive-danger-default: var(--mds-color-red-500);
|
|
39
|
+
--mds-bg-interactive-danger-default-light: var(--mds-color-red-50);
|
|
34
40
|
--mds-bg-interactive-danger-disabled: var(--mds-color-red-200);
|
|
35
41
|
--mds-bg-interactive-danger-hover: var(--mds-color-red-600);
|
|
36
42
|
--mds-bg-interactive-primary-active: var(--mds-color-blue-700);
|
|
37
43
|
--mds-bg-interactive-primary-default: var(--mds-color-blue-500);
|
|
44
|
+
--mds-bg-interactive-primary-default-light: var(--mds-color-blue-50);
|
|
38
45
|
--mds-bg-interactive-primary-disabled: var(--mds-color-blue-200);
|
|
39
46
|
--mds-bg-interactive-primary-hover: var(--mds-color-blue-600);
|
|
40
47
|
--mds-bg-interactive-secondary-default: var(--mds-color-gray-400);
|