├── media ├── gds-icon.png └── gds-icon.svg ├── specs ├── foundation │ ├── _media │ │ └── color-anatomy.png │ ├── color │ │ ├── index.md │ │ └── applying-colors.md │ ├── typography │ │ ├── index.md │ │ ├── applying-typography.md │ │ └── typography-tokens.md │ ├── structure │ │ ├── index.md │ │ ├── layout.md │ │ ├── breakpoint.md │ │ ├── applying-structure.md │ │ └── structure-tokens.md │ ├── index.md │ ├── tokens.md │ ├── components.md │ └── theme.md ├── api │ ├── primitive-tokens │ │ └── functions.md │ ├── composition │ │ ├── index.md │ │ └── color.md │ └── constants.md ├── getting-started │ └── setup.md ├── index.md └── components │ ├── index.md │ └── ms-button.md ├── tests ├── sass │ ├── ms-badge │ │ ├── _index.scss │ │ └── extend.test.scss │ ├── ms-button │ │ ├── _index.scss │ │ └── extend.test.scss │ ├── ms-form-field │ │ ├── _index.scss │ │ └── extend.test.scss │ ├── package.json │ ├── main.test.scss │ ├── pintig │ │ ├── _index.scss │ │ ├── primitive-tokens.test.scss │ │ ├── theme-get.test.scss │ │ ├── theme-merge.test.scss │ │ ├── tokens.test.scss │ │ ├── theme-tokens.test.scss │ │ ├── theme-properties.test.scss │ │ ├── color-setters.test.scss │ │ └── color-utils.test.scss │ ├── sass.test.ts │ ├── font-pack.test.scss │ └── with-html.test.scss └── web-components │ └── index.html ├── packages ├── pintig │ ├── sprite │ │ ├── _index.scss │ │ └── _sprite-mixins.scss │ ├── component │ │ ├── _index.scss │ │ ├── component-properties.ts │ │ ├── index.ts │ │ ├── _component-extend.scss │ │ └── component-base.ts │ ├── breakpoint │ │ ├── _index.scss │ │ └── _breakpoint-sentro.scss │ ├── tokens │ │ ├── _index.scss │ │ └── _token-methods.scss │ ├── struct │ │ ├── _index.scss │ │ ├── _struct-validators.scss │ │ ├── _struct-setters.scss │ │ └── _struct-utils.scss │ ├── README.md │ ├── typography │ │ ├── _index.scss │ │ ├── _typography-validators.scss │ │ ├── _typography-setters.scss │ │ └── _typography-utils.scss │ ├── color │ │ ├── _index.scss │ │ ├── _color-constants.scss │ │ ├── _color-validators.scss │ │ ├── _color-utils.scss │ │ └── _color-setters.scss │ ├── tools │ │ ├── _index.scss │ │ ├── _tools-helpers.scss │ │ ├── _tools-sentro.scss │ │ ├── _tools-string.scss │ │ ├── _tools-validation.scss │ │ └── _tools-color.scss │ ├── index.ts │ ├── theme │ │ ├── _index.scss │ │ ├── _theme-keys.scss │ │ ├── _theme-get.scss │ │ ├── _theme-merge.scss │ │ ├── _theme-tokens.scss │ │ └── _theme-properties.scss │ ├── package.json │ └── _index.scss ├── himig-components │ ├── helpers │ │ ├── _index.scss │ │ └── _states.scss │ ├── styles.scss │ ├── form-field │ │ ├── ms-form-field.styles.scss │ │ ├── ms-form-field.ts │ │ └── _form-field-base.scss │ ├── package.json │ ├── tsconfig.json │ └── button │ │ ├── _button-theme.scss │ │ ├── _button-base.scss │ │ ├── ms-button.ts │ │ └── ms-button.styles.scss └── himig │ ├── ms-card │ ├── _index.scss │ ├── _card-extend.scss │ ├── _card-theme.scss │ └── _card-styles.scss │ ├── ms-link │ ├── _index.scss │ ├── _link-base.scss │ ├── _link-theme.scss │ ├── _link-extend.scss │ └── _link-styles.scss │ ├── ms-list │ ├── _index.scss │ ├── _list-theme.scss │ ├── _list-extend.scss │ ├── _list-base.scss │ └── _list-styles.scss │ ├── ms-badge │ ├── _index.scss │ ├── _badge-base.scss │ ├── _badge-theme.scss │ ├── _badge-extend.scss │ └── _badge-styles.scss │ ├── ms-button │ ├── _index.scss │ ├── _button-extend.scss │ ├── _button-theme.scss │ ├── _button-base.scss │ └── _button-mixins.scss │ ├── ms-form-field │ ├── _index.scss │ ├── _form-field-extend.scss │ ├── _form-field-theme.scss │ └── _form-field-base.scss │ ├── ms-tick-field │ ├── _index.scss │ ├── _tick-field-extend.scss │ ├── _tick-field-theme.scss │ ├── _tick-field-base.scss │ └── _tick-field-styles.scss │ ├── ms-select-field │ ├── _index.scss │ ├── _select-field-extend.scss │ ├── _select-field-theme.scss │ └── _select-field-base.scss │ ├── ms-utils │ ├── _consts.scss │ ├── _index.scss │ ├── _util-helpers.scss │ └── _color-utils.scss │ ├── README.md │ ├── _init-font-pack.scss │ ├── yarn.lock │ └── package.json ├── config ├── tsconfig.sass.json └── tsconfig.lit.json ├── .editorconfig ├── scripts ├── tsconfig.json └── scss-to-lit.ts ├── jest.config.ts ├── gulpfile.ts ├── .github ├── workflows │ └── himig-ci.yml ├── ISSUE_TEMPLATE │ ├── himig-specifications.yml │ ├── himig-feature-requests.yml │ └── himig-bug-reports.yml └── CONTRIBUTING.md ├── .gitignore ├── rollup.config.mjs ├── README.md ├── tsconfig.json ├── LICENSE └── package.json /media/gds-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatteuSan/himig/HEAD/media/gds-icon.png -------------------------------------------------------------------------------- /specs/foundation/_media/color-anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatteuSan/himig/HEAD/specs/foundation/_media/color-anatomy.png -------------------------------------------------------------------------------- /tests/sass/ms-badge/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'extend.test'; -------------------------------------------------------------------------------- /tests/sass/ms-button/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'extend.test'; -------------------------------------------------------------------------------- /tests/sass/ms-form-field/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'extend.test'; -------------------------------------------------------------------------------- /packages/pintig/sprite/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'sprite-mixins'; -------------------------------------------------------------------------------- /packages/himig-components/helpers/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'states'; -------------------------------------------------------------------------------- /packages/pintig/component/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'component-extend'; -------------------------------------------------------------------------------- /packages/pintig/breakpoint/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'breakpoint-sentro'; -------------------------------------------------------------------------------- /specs/api/primitive-tokens/functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Primitive Token Functions 3 | slug: /api/primitive-tokens/functions 4 | --- 5 | # Primitive Token Functions -------------------------------------------------------------------------------- /packages/pintig/tokens/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'token-methods' as primitive-token-*; -------------------------------------------------------------------------------- /packages/himig/ms-card/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'card-styles'; 6 | @forward 'card-extend'; -------------------------------------------------------------------------------- /packages/himig/ms-link/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'link-styles'; 6 | @forward 'link-extend'; -------------------------------------------------------------------------------- /packages/himig/ms-list/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'list-styles'; 6 | @forward 'list-extend'; -------------------------------------------------------------------------------- /packages/himig/ms-badge/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'badge-styles'; 6 | @forward 'badge-extend'; -------------------------------------------------------------------------------- /packages/pintig/struct/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'struct-utils'; 6 | @forward 'struct-setters'; -------------------------------------------------------------------------------- /packages/himig/ms-button/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'button-styles'; 6 | @forward 'button-extend'; -------------------------------------------------------------------------------- /packages/himig-components/styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig'; 6 | 7 | @include himig.init(); -------------------------------------------------------------------------------- /packages/pintig/README.md: -------------------------------------------------------------------------------- 1 | # Pintig 2 | A higher-level core library for building design systems using Sentro at TeuLabs. 3 | 4 | ## Installation 5 | ```npm 6 | npm i @matteusan/pintig 7 | ``` -------------------------------------------------------------------------------- /packages/himig/ms-form-field/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'form-field-styles'; 6 | @forward 'form-field-extend'; -------------------------------------------------------------------------------- /packages/himig/ms-tick-field/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'tick-field-styles'; 6 | @forward 'tick-field-extend'; -------------------------------------------------------------------------------- /packages/pintig/typography/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'typography-setters'; 6 | @forward 'typography-utils'; -------------------------------------------------------------------------------- /packages/himig/ms-select-field/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'select-field-styles'; 6 | @forward 'select-field-extend'; -------------------------------------------------------------------------------- /packages/pintig/color/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'color-constants'; 6 | @forward 'color-setters'; 7 | @forward 'color-utils'; -------------------------------------------------------------------------------- /tests/sass/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@matteusan/himig": "file:../../packages/himig", 4 | "@matteusan/pintig": "file:../../packages/pintig", 5 | "sass-true": "^8.0.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/himig/ms-utils/_consts.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | $breakpoints: ('small', 'medium', 'large', 'xlarge'); -------------------------------------------------------------------------------- /packages/himig/ms-utils/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'layout-utils'; 6 | @forward 'color-utils'; 7 | @forward 'struct-utils'; 8 | @forward 'typography-utils'; -------------------------------------------------------------------------------- /config/tsconfig.sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": [ 4 | "packages/himig/**/*.ts", 5 | "tests/**/*.ts", 6 | "scripts/**/*.ts" 7 | ], 8 | "exclude": [ 9 | "./**/*.d.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/himig/ms-link/_link-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | .ms-link { 9 | @content; 10 | } 11 | } -------------------------------------------------------------------------------- /packages/pintig/component/component-properties.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | export function accessProperty(customProperty: string): string { 8 | return `--ms-${customProperty}`; 9 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [{*.scss, *.ts, *.js}] 4 | indent_style = space 5 | indent_size = 2 6 | ij_continuation_indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /packages/pintig/tools/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @forward 'tools-color'; 6 | @forward 'tools-helpers'; 7 | @forward 'tools-sentro'; 8 | @forward 'tools-string' as str-*; 9 | @forward 'tools-validation'; -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../config/tsconfig.lit.json", 3 | "compilerOptions": { 4 | "types": ["node"], 5 | "allowSyntheticDefaultImports": true, 6 | "target": "ES2022", 7 | "module": "ES2022" 8 | }, 9 | "exclude": [], 10 | "include": ["**/*"] 11 | } -------------------------------------------------------------------------------- /packages/pintig/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { MSComponent } from './component/types/ms-component'; 8 | import { MSComponentBase } from './component'; 9 | 10 | export { 11 | MSComponent, 12 | MSComponentBase 13 | } -------------------------------------------------------------------------------- /specs/foundation/color/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Color 3 | slug: /foundation/color 4 | --- 5 | # Color 6 | Color makes an element stand out visually. It provides identity, hierarchy, and depth to an element. 7 | 8 | Guideline MsButton: 9 | - [Applying Color](applying-colors.md) 10 | - [Color Tokens](color-tokens.md) -------------------------------------------------------------------------------- /packages/pintig/component/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { MSComponentBase } from './component-base'; 8 | import { accessProperty } from './component-properties'; 9 | 10 | export { 11 | MSComponentBase, 12 | accessProperty 13 | }; -------------------------------------------------------------------------------- /packages/pintig/tools/_tools-helpers.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'tools-validation' as validators; 6 | 7 | @function prioritize($priority, $fallback) { 8 | @if validators.is-empty($priority) { 9 | @return $fallback; 10 | } 11 | @return $priority; 12 | } -------------------------------------------------------------------------------- /packages/himig-components/form-field/ms-form-field.styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig' with ($module-mode: true); 6 | 7 | @use '../helpers'; 8 | @use 'form-field-base'; 9 | 10 | @include form-field-base.init(10px, 10px, 10px) { 11 | // 12 | } -------------------------------------------------------------------------------- /packages/pintig/sprite/_sprite-mixins.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | 7 | @mixin styles() { 8 | width: theme.key-create('sprite-width', var(--ms-sprite-size)); 9 | height: theme.key-create('sprite-height', var(--ms-sprite-size)); 10 | user-select: none; 11 | } -------------------------------------------------------------------------------- /tests/sass/main.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig'; 6 | @use 'pkg:sass-true' as true with ($terminal-output: true); 7 | 8 | // Core Modules 9 | @use 'pintig'; 10 | 11 | // Components 12 | @use 'ms-badge'; 13 | @use 'ms-button'; 14 | @use 'ms-form-field'; -------------------------------------------------------------------------------- /tests/sass/pintig/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'color-setters.test'; 6 | @use 'color-utils.test'; 7 | @use 'primitive-tokens.test'; 8 | @use 'theme-get.test'; 9 | @use 'theme-merge.test'; 10 | @use 'theme-properties.test'; 11 | @use 'theme-tokens.test'; 12 | @use 'tokens.test'; -------------------------------------------------------------------------------- /packages/himig-components/helpers/_states.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:selector'; 7 | 8 | @mixin state($state, $target: ()) { 9 | $selectors: ($state); 10 | @at-root :host(#{$state}) #{if(not pintig.is-empty($target), #{unquote($target)}, '')} { 11 | @content; 12 | } 13 | } -------------------------------------------------------------------------------- /packages/pintig/theme/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $expressive-inks: false !default; 6 | 7 | @forward 'theme-keys' as key-*; 8 | @forward 'theme-tokens' as token-*; 9 | @forward 'theme-setters' with ($expressive-inks: $expressive-inks); 10 | @forward 'theme-properties'; 11 | @forward 'theme-get'; 12 | @forward 'theme-merge'; -------------------------------------------------------------------------------- /specs/foundation/typography/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Typography 3 | slug: /foundation/typography 4 | --- 5 | # Typography 6 | Typography defines how you deal text content in an element. This is particularly important to making the text legible, 7 | hierarchical, and breathing. 8 | 9 | Guideline MsButton: 10 | - [Applying Typography](applying-typography.md) 11 | - [Typography Tokens](typography-tokens.md) -------------------------------------------------------------------------------- /config/tsconfig.lit.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "../packages/himig-components", 5 | "outDir": "../packages/himig-components", 6 | "plugins": [ 7 | { 8 | "name": "ts-lit-plugin", 9 | "strict": true 10 | } 11 | ] 12 | }, 13 | "include": ["../packages/himig-components/**/*.ts"], 14 | "exclude": [] 15 | } 16 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /** @type {import('jest').Config} */ 8 | module.exports = { 9 | clearMocks: true, 10 | rootDir: "./", 11 | modulePaths: ["/node_modules"], 12 | globals: { 13 | "ts-jest": { 14 | tsconfig: "./config/tsconfig.sass.json" 15 | } 16 | }, 17 | verbose: true, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/pintig/color/_color-constants.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $fill-default-opacity: 0%; 6 | $fill-hover-opacity: 20%; 7 | $fill-focus-opacity: 20%; 8 | $fill-active-opacity: 33%; 9 | 10 | $border-default-opacity: 0%; 11 | $border-hover-opacity: 2%; 12 | $border-focus-opacity: 2%; 13 | $border-active-opacity: 3.3%; 14 | 15 | $outline-focus-opacity: 50%; -------------------------------------------------------------------------------- /specs/foundation/structure/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Structure 3 | slug: /foundation/structure 4 | --- 5 | # Structure 6 | Structure defines the physical appearance of an element that isn't related to the soft properties like color and 7 | typography. 8 | 9 | Guideline MsButton: 10 | - [Applying Structure](applying-structure.md) 11 | - [Breakpoint](breakpoint.md) 12 | - [Layout](layout.md) 13 | - [Structure Tokens](structure-tokens.md) -------------------------------------------------------------------------------- /tests/sass/sass.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | const path = require('node:path'); 8 | const sassTest = require('sass-true'); 9 | const scss = require('sass'); 10 | const filePath = path.join(__dirname, './main.test.scss'); 11 | 12 | sassTest.runSass({ describe, it }, filePath, { 13 | importers: [new scss.NodePackageImporter()], 14 | verbose: true 15 | }); -------------------------------------------------------------------------------- /packages/himig/ms-link/_link-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | hover: 'accent-400', 8 | focus: 'accent-400', 9 | active: 'accent-400' 10 | ), 11 | ink: 'accent-400', 12 | ); 13 | 14 | $init-struct: (); 15 | 16 | $init-typography: ( 17 | family: inherit, 18 | size: inherit, 19 | weight: inherit, 20 | line-height: inherit 21 | ); 22 | 23 | $init-settings: (); -------------------------------------------------------------------------------- /specs/api/composition/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Composition API 3 | slug: /api/composition 4 | --- 5 | # Composition API 6 | Himig lets you compose complex components that can't be solved by utility classes alone. By using the composition API, 7 | it lets you create components with the same API patterns as Himig's primitive components. 8 | 9 | Here are the composition APIs you can work with: 10 | - [Color](color.md) 11 | - [Structure](structure.md) 12 | - [Typography](typography.md) -------------------------------------------------------------------------------- /packages/pintig/theme/_theme-keys.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/sentro'; 6 | 7 | @function create($key, $value) { 8 | @return sentro.key-create($key, $value); 9 | } 10 | 11 | @function get($key) { 12 | @return sentro.key-get($key); 13 | } 14 | 15 | @function get-raw($key) { 16 | @return sentro.key-get-raw($key); 17 | } 18 | 19 | @function check($key) { 20 | @return sentro.key-check($key); 21 | } 22 | 23 | @mixin bind($key, $value) { 24 | @include sentro.key-bind($key, $value); 25 | } -------------------------------------------------------------------------------- /packages/himig/ms-badge/_badge-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | .ms-badge { 9 | display: flex; 10 | flex-flow: row nowrap; 11 | 12 | @content; 13 | 14 | .ms-badge__icon { 15 | // 16 | } 17 | 18 | .ms-badge__label { 19 | @include pintig.typography-apply('badge', ( 20 | family: inherit, 21 | size: inherit, 22 | weight: inherit, 23 | line-height: inherit 24 | )); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /gulpfile.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | const { src, dest, series } = require('gulp'); 8 | const sassBase = require('sass'); 9 | const sass = require('gulp-sass')(sassBase); 10 | 11 | const testSass = () => { 12 | return src('tests/sass/with-html.test.scss') 13 | .pipe(sass({ 14 | loadPath: ['node_modules'], 15 | importers: [sassBase.NodePackageImporter()], 16 | verbose: true, 17 | }).on('error', sass.logError)) 18 | .pipe(dest('tests/sass')); 19 | } 20 | 21 | exports.default = series( 22 | testSass 23 | ); -------------------------------------------------------------------------------- /packages/himig/ms-badge/_badge-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: 'accent-400', 7 | ink: 'accent-ink', 8 | border: 'accent-400', 9 | ); 10 | 11 | $init-struct: ( 12 | width: ( 13 | default: 100%, 14 | max: max-content 15 | ), 16 | padding: ('xs' 'sm'), 17 | radius: 'small', 18 | border-width: 'xs', 19 | border-style: solid, 20 | ); 21 | 22 | $init-typography: ( 23 | family: 'global', 24 | size: 'small', 25 | weight: 'normal', 26 | line-height: 'condensed' 27 | ); 28 | 29 | $init-settings: (); -------------------------------------------------------------------------------- /packages/pintig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@matteusan/pintig", 3 | "version": "1.10.0", 4 | "description": "A higher-level core library for building design systems using Sentro at TeuLabs.", 5 | "main": "_index.scss", 6 | "repository": "https://github.com/MatteuSan/himig", 7 | "author": "Matteu ", 8 | "license": "MIT", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "exports": { 13 | ".": { 14 | "sass": "./_index.scss" 15 | }, 16 | "./**/*.scss": { 17 | "sass": "./**/*.scss" 18 | } 19 | }, 20 | "dependencies": { 21 | "@matteusan/sentro": "^1.4.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/himig-ci.yml: -------------------------------------------------------------------------------- 1 | name: Himig CI 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | pull_request: 7 | branches: ['**'] 8 | 9 | jobs: 10 | test-sass: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [ 20.x, 21.x ] 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Setup pnpm 18 | uses: pnpm/action-setup@v4 19 | with: 20 | version: 10 21 | - name: Use NodeJS version ${{ matrix.node-version }} 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | cache: 'pnpm' 26 | - run: pnpm i 27 | - run: pnpm test:sass -------------------------------------------------------------------------------- /tests/sass/font-pack.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig/init-font-pack' as font-pack; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Font Pack [mixin]') { 9 | @include true.it('should load font pack') { 10 | @include true.assert { 11 | @include true.output($selector: false) { 12 | @include font-pack.load(( 13 | 'small': 'family.sans-serif', 14 | )); 15 | } 16 | @include true.expect($selector: false) { 17 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"); 18 | } 19 | } 20 | } 21 | } 22 | 23 | @include true.report(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Package folders 2 | node_modules 3 | **/node_modules 4 | vendor 5 | 6 | # Build directories 7 | build 8 | dist 9 | 10 | # Dump directory 11 | dump 12 | 13 | # Workspaces 14 | .idea 15 | .vscode 16 | 17 | # Test build files 18 | tests/**/*.test.css 19 | tests/**/*.test.js 20 | 21 | # Package build files 22 | packages/**/*.css 23 | packages/**/*.css.map 24 | packages/**/*.js 25 | packages/**/*.js.map 26 | packages/**/*.d.ts.map 27 | packages/**/*.d.ts 28 | packages/**/*.style.ts 29 | packages/**/*.styles.ts 30 | packages/himig-components/styles.ts 31 | packages/himig-components/styles.js 32 | 33 | # Test byproducts 34 | **/.coverage 35 | **/coverage 36 | 37 | # Yarn 38 | .yarn 39 | 40 | # Junk files 41 | .DS_Store 42 | */**/.DS_Store -------------------------------------------------------------------------------- /packages/pintig/tools/_tools-sentro.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/sentro'; 6 | 7 | /// Converts px values to rem values. 8 | /// @param {int} $px 9 | /// @return {int} rem value. 10 | @function to-rem($px) { 11 | @return sentro.to-rem($px); 12 | } 13 | 14 | /// Prefixes properties 15 | /// @param {string} $property 16 | /// @param {*} $value 17 | /// @return {*} generated CSS. 18 | @mixin prefix($property, $value) { 19 | @include sentro.prefix($property, $value); 20 | } 21 | 22 | /// Line clamp mixin. 23 | /// @param {string|int} $lines 24 | /// @return {*} queried line clamp styles. 25 | @mixin break($lines) { 26 | @include sentro.line-clamp($lines); 27 | } -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import resolve from '@rollup/plugin-node-resolve'; 8 | import babel from '@rollup/plugin-babel'; 9 | import { terser } from 'rollup-plugin-terser'; 10 | import multiEntry from '@rollup/plugin-multi-entry'; 11 | 12 | /** @type {import('rollup').RollupOptions} */ 13 | export default { 14 | input: [ 15 | 'packages/himig-components/button/ms-button.js', 16 | // 'packages/himig-components/form-field/ms-form-field.js' 17 | ], 18 | output: { 19 | dir: 'packages/himig-components', 20 | format: 'esm', 21 | }, 22 | plugins: [ 23 | resolve(), 24 | babel({ babelHelpers: 'bundled' }), 25 | terser() 26 | ] 27 | }; -------------------------------------------------------------------------------- /packages/pintig/theme/_theme-get.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../tools'; 6 | @use 'sass:map'; 7 | @use 'sass:list'; 8 | 9 | /// Retrieves a value from a theme map by key. 10 | /// @param {map} $map 11 | /// @param {string} $key 12 | /// @return {*} value. 13 | @function theme-get($map, $key, $keys...) { 14 | $final-map: map.get($map, $key, $keys...); 15 | @if tools.is-type($final-map, 'map', $errors: false) { 16 | $map-keys: map.keys($final-map); 17 | @if not list.index($map-keys, 'default') { 18 | @error 'Missing `default` key in theme map. Please specify a key, or add a `default` key with a value.'; 19 | } 20 | @return map.get($final-map, 'default'); 21 | } 22 | @return $final-map; 23 | } -------------------------------------------------------------------------------- /packages/himig-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@matteusan/himig-components", 3 | "version": "0.0.1", 4 | "description": "A web-components implementation of MatteuSan's design system, Himig.", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/MatteuSan/himig.git" 13 | }, 14 | "author": "MatteuSan", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/MatteuSan/himig/issues" 18 | }, 19 | "homepage": "https://github.com/MatteuSan/himig#readme", 20 | "peerDependencies": { 21 | "@matteusan/pintig": "^1.5.1", 22 | "lit": "^3.1.3", 23 | "sass": "^1.75.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /specs/foundation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Foundation 3 | slug: /foundation 4 | --- 5 | # Foundation 6 | Himig's foundation is crafted from the ground up to serve as solid ground the for entire design system. 7 | 8 | **[Tokens](tokens.md)**
9 | All about how Himig uses tokens to power itself. 10 | 11 | **[Color](color/index.md)**
12 | See how Himig uses colors to create beautiful, living, and harmonious interfaces. 13 | 14 | **[Structure](structure/index.md)**
15 | Learn how to create an element with visual and rhythmic structure. 16 | 17 | **[Theme](theme.md)**
18 | Learn the fundamentals of handling color, structure, and typography in a harmonious way. 19 | 20 | **[Typography](typography/index.md)**
21 | Learn how to use typography to get written content across in a meaningful way. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | Be sure to have [NodeJS](https://nodejs.org) LTS installed. Any other version will risk the project breaking in 3 | development. 4 | This design system is dependent on the CSS preprocessor, [Sass](https://sass-lang.com). When using Sass, **be sure to 5 | use DartSass instead of the now deprecated LibSass (node-sass)**. 6 | 7 | ## Setup 8 | ```sh 9 | $ npm install @matteusan/himig --save 10 | 11 | # or 12 | 13 | $ yarn add @matteusan/himig 14 | ``` 15 | ```scss 16 | // main.scss 17 | 18 | @use 'pkg:@matteusan/sentro'; 19 | @use '@matteusan/himig/ms-button'; 20 | 21 | @include himig.init() { 22 | @include ms-button.render(); 23 | } 24 | ``` 25 | 26 | ## Contributing 27 | Contributing guidelines are located [here](https://github.com/MatteuSan/himig/tree/main/.github/CONTRIBUTING.md). -------------------------------------------------------------------------------- /tests/sass/pintig/primitive-tokens.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Primitive Token [module]') { 9 | @include true.it('should retrieve a token from the primitive token registry.') { 10 | $test: pintig.primitive-token-get('ms-red.400'); 11 | @include true.assert-equal($test, oklch(65% 0.25 28.5deg)); 12 | } 13 | 14 | @include true.it('should soft validate if the query is a valid primitive token.') { 15 | $test: pintig.primitive-token-check('ms-red.400'); 16 | $test2: pintig.primitive-token-check('ms-red.900'); 17 | @include true.assert-equal($test, true); 18 | @include true.assert-equal($test2, false); 19 | } 20 | } 21 | 22 | @include true.report(); -------------------------------------------------------------------------------- /packages/himig/README.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | Be sure to have [NodeJS](https://nodejs.org) LTS installed. Any other version will risk the project breaking in 3 | development. 4 | This design system is dependent on the CSS preprocessor, [Sass](https://sass-lang.com). When using Sass, **be sure to 5 | use DartSass instead of the now deprecated LibSass (node-sass)**. 6 | 7 | ## Setup 8 | ```sh 9 | $ npm i @matteusan/himig @matteusan/pintig --save 10 | 11 | # or 12 | 13 | $ pnpm add @matteusan/himig @matteusan/pintig 14 | ``` 15 | ```scss 16 | // main.scss 17 | 18 | @use 'pkg:@matteusan/himig'; 19 | @use 'pkg:@matteusan/himig/ms-button'; 20 | 21 | @include himig.init { 22 | @include ms-button.render(); 23 | } 24 | ``` 25 | 26 | ## Contributing 27 | Contributing guidelines are located [here](https://github.com/MatteuSan/himig/tree/main/.github/CONTRIBUTING.md). -------------------------------------------------------------------------------- /packages/pintig/typography/_typography-validators.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:list'; 6 | 7 | /// Valid component props 8 | /// @access private 9 | $_valid-props: ('family', 'size', 'weight', 'line-height', 'squeeze'); 10 | 11 | /// Validates a set of typography props. 12 | /// @param {map} $props 13 | /// @access protected 14 | @mixin validate-typography-props($props) { 15 | @each $prop in $props { 16 | @if _is-typography-prop($prop) { 17 | } 18 | } 19 | @content; 20 | } 21 | 22 | /// Validates a typography prop. 23 | /// @access private 24 | @function _is-typography-prop($query) { 25 | @if not list.index($_valid-props, $query) { 26 | @error 'Invalid/unrecognized prop: #{$query}. Please use one of the following props: #{$_valid-props}.' 27 | } 28 | @return true; 29 | } -------------------------------------------------------------------------------- /packages/himig/ms-list/_list-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'surface-600', 8 | hover: 'surface-500', 9 | focus: 'surface-500', 10 | active: 'surface-400' 11 | ), 12 | ink: 'surface-ink', 13 | border: ( 14 | default: 'surface-600', 15 | hover: 'surface-500', 16 | focus: 'surface-500', 17 | active: 'surface-400' 18 | ), 19 | ); 20 | 21 | $init-struct: ( 22 | width: ( 23 | default: 100%, 24 | min: 140px, 25 | max: max-content 26 | ), 27 | padding: 'md', 28 | radius: 'small', 29 | border-width: 'xs', 30 | border-style: solid, 31 | gap: 'sm' 32 | ); 33 | 34 | $init-typography: ( 35 | family: 'global', 36 | size: 'body', 37 | weight: 'normal', 38 | line-height: 'condensed' 39 | ); 40 | 41 | $init-settings: (); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": false, 4 | "declaration": true, 5 | "declarationMap": false, 6 | "downlevelIteration": true, 7 | "experimentalDecorators": true, 8 | "importHelpers": true, 9 | "module": "es2015", 10 | "moduleResolution": "node", 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitAny": true, 13 | "noImplicitOverride": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noPropertyAccessFromIndexSignature": true, 17 | "noUnusedLocals": true, 18 | "paths": { 19 | "@matteusan/himig-components/*": ["./packages/himig-components/*"] 20 | }, 21 | "sourceMap": true, 22 | "inlineSources": true, 23 | "strict": true, 24 | "strictNullChecks": false, 25 | "target": "es2021", 26 | "types": ["lit", "node", "jest"] 27 | }, 28 | } -------------------------------------------------------------------------------- /tests/sass/pintig/theme-get.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Theme Get [function]') { 9 | @include true.it('should retrieve a theme value from a map') { 10 | $theme: ( 11 | fill: ( 12 | default: 'ms-orange.400', 13 | hover: 'ms-orange.200' 14 | ), 15 | ink: 'ms-neutral.200', 16 | border: 'ms-orange.400' 17 | ); 18 | @include true.assert-equal(pintig.theme-get($theme, 'fill'), 'ms-orange.400'); 19 | @include true.assert-equal(pintig.theme-get($theme, 'fill', 'hover'), 'ms-orange.200'); 20 | @include true.assert-equal(pintig.theme-get($theme, 'ink'), 'ms-neutral.200'); 21 | @include true.assert-equal(pintig.theme-get($theme, 'border'), 'ms-orange.400'); 22 | } 23 | } -------------------------------------------------------------------------------- /packages/pintig/tools/_tools-string.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:list'; 6 | @use 'sass:string'; 7 | 8 | /// Splits a string from a given separator. 9 | /// Source: https://stackoverflow.com/a/42295154 10 | /// @param {string} $string 11 | /// @param {string} $separator 12 | /// @return {list} returns split string list. 13 | @function split($string, $separator) { 14 | $split-list: (); 15 | $index: string.index($string, $separator); 16 | 17 | @while $index != null { 18 | $item: string.slice($string, 1, $index - 1); 19 | $split-list: list.append($split-list, $item, comma); 20 | $string: string.slice($string, $index + string.length($separator)); 21 | $index: string.index($string, $separator); 22 | } 23 | 24 | $split-list: list.append($split-list, $string, comma); 25 | 26 | @return $split-list; 27 | } -------------------------------------------------------------------------------- /packages/himig/_init-font-pack.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:map'; 6 | @use 'sass:list'; 7 | 8 | @mixin load($query) { 9 | $family-list: (); 10 | @each $key, $family in $query { 11 | $family-list: list.append($family-list, $family); 12 | } 13 | 14 | @if list.index($family-list, 'family.sans-serif') { 15 | @import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); 16 | } 17 | 18 | @if list.index($family-list, 'family.serif') { 19 | @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+Display:ital,wght@0,100..900;1,100..900&display=swap'); 20 | } 21 | 22 | @if list.index($family-list, 'family.mono') { 23 | @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap'); 24 | } 25 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/himig-specifications.yml: -------------------------------------------------------------------------------- 1 | name: Specifications 2 | description: ONLY FOR INTERNAL STAFF! Propose a specification document. 3 | title: "[SPEC] " 4 | labels: ["specs"] 5 | assignees: 6 | - MatteuSan 7 | body: 8 | - type: textarea 9 | id: spec-description 10 | attributes: 11 | label: Specification Description 12 | description: Tell us a bit more about your specification proposal. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: spec-context 17 | attributes: 18 | label: Specification Context 19 | description: Why does this matter? Who will benefit from this and in what way? 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: spec-details 24 | attributes: 25 | label: Specification Details 26 | description: A brief detailing of how the specification will work out. 27 | validations: 28 | required: true -------------------------------------------------------------------------------- /specs/getting-started/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Setup 3 | slug: /getting-started/setup 4 | --- 5 | # Setup 6 | A guide to setting up the development environment for Himig. 7 | 8 | ## Installation 9 | ```sh 10 | # PNPM 11 | $ pnpm add @matteusan/himig @matteusan/pintig sass 12 | 13 | # NPM 14 | $ npm i @matteusan/himig @matteusan/pintig sass 15 | ``` 16 | 17 | ## Instantiation 18 | In your project's sass entry file. Initialize the design system. 19 | 20 | ```scss 21 | @use 'pkg:@matteusan/himig'; 22 | 23 | @include himig.init() { 24 | // ... 25 | } 26 | ``` 27 | 28 | Whenever you feel the need to include a component, instantiate it inside the Himig initializer. 29 | 30 | ```scss 31 | @use 'pkg:@matteusan/himig'; 32 | @use 'pkg:@matteusan/himig/ms-button'; 33 | 34 | @include himig.init() { 35 | @include ms-button.render(); 36 | } 37 | ``` 38 | 39 | ### Theme Configuration 40 | To see how to configure the theme, go to [this page](../foundation/theme.md). -------------------------------------------------------------------------------- /packages/pintig/theme/_theme-merge.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:map'; 6 | @use 'sass:list'; 7 | @use 'sass:meta'; 8 | 9 | /// Merges two or more Sass maps into a single map. 10 | /// @param {map} $base-map 11 | /// @param {map...} $modifying-maps 12 | /// @return {map} The merged map. 13 | @function theme-merge($base-map, $modifying-maps...) { 14 | $final-merged-map: (); 15 | @each $modifying-map in $modifying-maps { 16 | $final-merged-map: map.merge($final-merged-map, _smart-merge($base-map, $modifying-map)); 17 | } 18 | @return $final-merged-map; 19 | } 20 | 21 | /// Merges two maps into one based on the GS theme map spec. 22 | /// @access private 23 | /// @param {map} $base-map 24 | /// @param {map} $modifying-map 25 | /// @return {map} The merged map. 26 | @function _smart-merge($base-map, $modifying-map) { 27 | @return map.deep-merge($base-map, $modifying-map); 28 | } -------------------------------------------------------------------------------- /packages/pintig/struct/_struct-validators.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:map'; 6 | @use 'sass:list'; 7 | 8 | /// Valid component props 9 | /// @access private 10 | $_valid-props: ( 11 | 'width', 12 | 'height', 13 | 'radius', 14 | 'padding', 15 | 'margin', 16 | 'gap', 17 | 'shadow', 18 | 'border-style', 19 | 'border-width' 20 | ); 21 | 22 | /// Validates a set of structure props. 23 | /// @param {map} $props 24 | /// @access protected 25 | @mixin validate-struct-props($props) { 26 | @each $prop in $props { 27 | @if _is-struct-prop($prop) { } 28 | } 29 | @content; 30 | } 31 | 32 | /// Validates a structure prop. 33 | /// @access private 34 | @function _is-struct-prop($query) { 35 | @if not list.index($_valid-props, $query) { 36 | @error 'Invalid/unrecognized prop: #{$query}. Please use one of the following props: #{$_valid-props}.' 37 | } 38 | @return true; 39 | } -------------------------------------------------------------------------------- /tests/sass/pintig/theme-merge.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Theme Merge [module]') { 9 | @include true.it('should merge a theme map') { 10 | $init-theme: ( 11 | fill: ( 12 | default: 'ms-orange.400', 13 | hover: 'ms-orange.300' 14 | ), 15 | ink: 'ms-orange.800', 16 | border: ( 17 | default: 'ms-orange.400', 18 | hover: 'ms-orange.300' 19 | ) 20 | ); 21 | $user-theme: ( 22 | fill: 'ms-red.400', 23 | ); 24 | $theme: pintig.theme-merge($init-theme, $user-theme); 25 | @include true.assert-equal($theme, ( 26 | fill: 'ms-red.400', 27 | ink: 'ms-orange.800', 28 | border: ( 29 | default: 'ms-orange.400', 30 | hover: 'ms-orange.300' 31 | ) 32 | )); 33 | } 34 | } 35 | 36 | @include true.report(); -------------------------------------------------------------------------------- /packages/himig/ms-card/_card-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('card', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('card', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('card', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('card', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/himig/ms-link/_link-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('link', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('link', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('link', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('link', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/himig/ms-list/_list-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('list', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('list', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('list', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('list', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/pintig/breakpoint/_breakpoint-sentro.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../tools'; 6 | @use 'pkg:@matteusan/sentro'; 7 | @use 'sass:map'; 8 | @use 'sass:meta'; 9 | 10 | $_registry: (); 11 | 12 | @mixin config($map: (), $breakpoints...) { 13 | @if not tools.is-empty($map) { 14 | @each $key, $value in $map { 15 | $_registry: map.set($_registry, $key, $value) !global; 16 | } 17 | } 18 | @if meta.keywords($breakpoints) { 19 | @each $key, $value in meta.keywords($breakpoints) { 20 | $_registry: map.set($_registry, $key, $value) !global; 21 | } 22 | } 23 | @include sentro.breakpoint-config($map, $breakpoints...); 24 | } 25 | 26 | @mixin create($query, $context: 'min', $property: 'width') { 27 | @include sentro.breakpoint-create($query, $context, $property) { 28 | @content; 29 | } 30 | } 31 | 32 | /// @access protected 33 | @function registry-get() { 34 | @return $_registry; 35 | } -------------------------------------------------------------------------------- /packages/himig/ms-badge/_badge-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('badge', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('badge', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('badge', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('badge', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/pintig/theme/_theme-tokens.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/sentro'; 6 | @use '../tools'; 7 | 8 | @function get($query) { 9 | @return sentro.token-get($query); 10 | } 11 | 12 | @function get-raw($query) { 13 | @return sentro.token-get-raw($query); 14 | } 15 | 16 | @function switch($query, $fallback: ()) { 17 | @if not check($query) == true and not tools.is-empty($fallback) { 18 | @return $fallback; 19 | } 20 | @return sentro.token-switch($query); 21 | } 22 | 23 | @function switch-raw($query, $fallback: ()) { 24 | @if check($query) == true { 25 | @return sentro.token-get-raw($query); 26 | } 27 | @if not tools.is-empty($fallback) { 28 | @return $fallback; 29 | } 30 | @return $query; 31 | } 32 | 33 | @function check($query) { 34 | @return sentro.token-check($query); 35 | } 36 | 37 | @mixin config($map: (), $tokens...) { 38 | @include sentro.token-config($map, $tokens...); 39 | } -------------------------------------------------------------------------------- /packages/himig-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "es2020", 5 | "lib": ["es2021", "DOM", "DOM.Iterable"], 6 | "declaration": true, 7 | "declarationMap": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "outDir": "./", 11 | "rootDir": "./src", 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitAny": true, 18 | "noImplicitThis": true, 19 | "moduleResolution": "node", 20 | "allowSyntheticDefaultImports": true, 21 | "experimentalDecorators": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "noImplicitOverride": true, 24 | "plugins": [ 25 | { 26 | "name": "ts-lit-plugin", 27 | "strict": true 28 | } 29 | ], 30 | "types": ["mocha"] 31 | }, 32 | "include": ["src/**/*.ts"], 33 | "exclude": [] 34 | } -------------------------------------------------------------------------------- /tests/sass/ms-badge/extend.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig'; 6 | @use 'pkg:@matteusan/himig/ms-badge'; 7 | @use 'pkg:sass-true' as true; 8 | 9 | @include himig.init { 10 | @include true.describe('MS Badge Extend [mixin]') { 11 | @include true.it('should extend the styles of a ms-badge component') { 12 | @include true.assert { 13 | @include true.output { 14 | @include ms-badge.extend('large') { 15 | @include ms-badge.struct(( 16 | padding: ('lg' 'xl') 17 | )); 18 | @include ms-badge.typography(( 19 | size: 'supertitle' 20 | )); 21 | } 22 | } 23 | @include true.expect { 24 | .ms-badge.is-large { 25 | --ms-badge-padding: 1.2rem 2.3rem; 26 | --ms-badge-size: var(--ms-size-supertitle); 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/himig/ms-button/_button-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('button', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('button', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('button', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('button', $type-map); 35 | } -------------------------------------------------------------------------------- /tests/sass/ms-button/extend.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig'; 6 | @use 'pkg:@matteusan/himig/ms-button'; 7 | @use 'pkg:sass-true' as true; 8 | 9 | @include himig.init { 10 | @include true.describe('MS Button Extend [mixin]') { 11 | @include true.it('should extend the styles of a ms-button component') { 12 | @include true.assert { 13 | @include true.output { 14 | @include ms-button.extend('large') { 15 | @include ms-button.struct(( 16 | padding: ('lg' 'xl') 17 | )); 18 | @include ms-button.typography(( 19 | size: 'supertitle' 20 | )); 21 | } 22 | } 23 | @include true.expect { 24 | .ms-button.is-large { 25 | --ms-button-padding: 1.2rem 2.3rem; 26 | --ms-button-size: var(--ms-size-supertitle); 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/himig/ms-form-field/_form-field-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('form-field', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('form-field', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('form-field', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('form-field', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/himig/ms-tick-field/_tick-field-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('tick-field', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('tick-field', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('tick-field', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('tick-field', $type-map); 35 | } -------------------------------------------------------------------------------- /packages/pintig/color/_color-validators.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:list'; 6 | 7 | /// Valid component props 8 | /// @access private 9 | $_valid-props: ('fill', 'ink', 'border'); 10 | $_valid-colors: ( 11 | inherit, 12 | currentColor, 13 | transparent, 14 | rgba(0, 0, 0, 0), 15 | rgba(0 0 0 / 0) 16 | ); 17 | 18 | /// Validates a set of color props. 19 | /// @param {map} $props 20 | /// @access protected 21 | @mixin validate-color-props($props) { 22 | @each $prop in $props { 23 | @if _is-color-prop($prop) { } 24 | } 25 | @content; 26 | } 27 | 28 | /// Validates a color query. 29 | /// @access private 30 | @function _is-valid-color($color) { 31 | // 32 | } 33 | 34 | /// Validates a color prop. 35 | /// @access private 36 | @function _is-color-prop($query) { 37 | @if not list.index($_valid-props, $query) { 38 | @error 'Invalid color prop: #{$query}. Expecting a valid color prop: #{$_valid-props}.'; 39 | } 40 | @return true; 41 | } -------------------------------------------------------------------------------- /packages/himig/ms-select-field/_select-field-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | /// Component variant maker. 8 | /// @param {string} $variants 9 | /// @return {*} extension class. 10 | @mixin extend($variants) { 11 | @include pintig.component-extend('select-field', $variants) { 12 | @content; 13 | } 14 | } 15 | 16 | /// Color variant map. 17 | /// @param {map} $color-map 18 | /// @return {*} color styles. 19 | @mixin color($color-map) { 20 | @include pintig.color-bind('select-field', $color-map); 21 | } 22 | 23 | /// Structure variant mixin. 24 | /// @param {map} $struct-map 25 | /// @return {*} struct styles. 26 | @mixin struct($struct-map) { 27 | @include pintig.struct-bind('select-field', $struct-map); 28 | } 29 | 30 | /// Typography variant mixin. 31 | /// @param {map} $type-map 32 | /// @return {*} typography styles. 33 | @mixin typography($type-map) { 34 | @include pintig.typography-bind('select-field', $type-map); 35 | } -------------------------------------------------------------------------------- /tests/sass/ms-form-field/extend.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig'; 6 | @use 'pkg:@matteusan/himig/ms-form-field'; 7 | @use 'pkg:sass-true' as true; 8 | 9 | @include himig.init { 10 | @include true.describe('MS Form Field Extend [mixin]') { 11 | @include true.it('should extend the styles of a ms-form-field component') { 12 | @include true.assert { 13 | @include true.output { 14 | @include ms-form-field.extend('large') { 15 | @include ms-form-field.struct(( 16 | padding: ('lg' 'xl') 17 | )); 18 | @include ms-form-field.typography(( 19 | size: 'supertitle' 20 | )); 21 | } 22 | } 23 | @include true.expect { 24 | .ms-form-field.is-large { 25 | --ms-form-field-padding: 1.2rem 2.3rem; 26 | --ms-form-field-size: var(--ms-size-supertitle); 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/himig/ms-button/_button-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'accent-400', 8 | hover: 'accent-400', 9 | active: 'accent-300', 10 | focus: 'accent-400', 11 | disabled: 'disabled', 12 | ), 13 | ink: ( 14 | default: 'accent-ink', 15 | hover: 'accent-ink', 16 | active: 'accent-ink', 17 | focus: 'accent-ink', 18 | disabled: 'disabled-ink', 19 | ), 20 | border: ( 21 | default: 'accent-400', 22 | hover: 'accent-400', 23 | active: 'accent-300', 24 | focus: 'accent-400', 25 | disabled: 'disabled', 26 | ), 27 | ); 28 | 29 | $init-struct: ( 30 | width: ( 31 | default: 100%, 32 | min: 50px, 33 | max: max-content 34 | ), 35 | padding: ('md' 'lg'), 36 | radius: 'small', 37 | border-width: 'xs', 38 | border-style: solid, 39 | gap: 'xs' 40 | ); 41 | 42 | $init-typography: ( 43 | family: 'global', 44 | size: 'body', 45 | weight: 'bold', 46 | line-height: 1.25rem 47 | ); 48 | 49 | $init-settings: (); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 MatteuSan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/himig/ms-badge/_badge-styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'badge-base' as base; 7 | @use 'badge-theme' as theme; 8 | 9 | @mixin render( 10 | $color: (), 11 | $struct: (), 12 | $typography: (), 13 | $settings: () 14 | ) { 15 | $color: pintig.theme-merge(theme.$init-color, $color); 16 | $struct: pintig.theme-merge(theme.$init-struct, $struct); 17 | $typography: pintig.theme-merge(theme.$init-typography, $typography); 18 | $settings: pintig.theme-merge(theme.$init-settings, $settings); 19 | 20 | @layer components { 21 | @include base.init() { 22 | @include pintig.color-apply('badge', $color); 23 | @include pintig.struct-apply('badge', $struct); 24 | @include pintig.typography-bind('badge', $typography); 25 | 26 | &:where(.is-outlined) { 27 | @include pintig.color-bind('badge', ( 28 | fill: transparent, 29 | ink: pintig.theme-get($color, 'fill'), 30 | border: pintig.theme-get($color, 'border') 31 | )); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /tests/sass/pintig/tokens.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Token [module]') { 9 | @include true.it('Creates a set of tokens') { 10 | @include true.assert { 11 | @include true.output { 12 | :root { 13 | @include pintig.token-config( 14 | $surface: ( 15 | 300: #700, 16 | 400: #400 17 | ) 18 | ); 19 | } 20 | } 21 | @include true.expect { 22 | :root { 23 | --ms-surface-300: #700; 24 | --ms-surface-400: #400; 25 | } 26 | 27 | } 28 | } 29 | } 30 | 31 | @include true.it('should query a token') { 32 | $token: pintig.token-get('surface-300'); 33 | @include true.assert-equal($token, var(--ms-surface-300)); 34 | } 35 | 36 | @include true.it('should query a token\'s raw value') { 37 | $token: pintig.token-get-raw('surface-300'); 38 | @include true.assert-equal($token, #700); 39 | } 40 | } 41 | 42 | @include true.report(); -------------------------------------------------------------------------------- /tests/web-components/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | Himig Components Testing 12 | 13 | 14 | 19 | 20 | 21 |
22 | MSButton 23 | MSButton 24 | MSButton 25 |
26 | 27 |
28 | MSButton 29 | MSButton 30 | MSButton 31 |
32 | 33 |
34 | MSButton 35 | MSButton 36 |
37 | 38 | -------------------------------------------------------------------------------- /packages/himig/yarn.lock: -------------------------------------------------------------------------------- 1 | # This file is generated by running "yarn install" inside your project. 2 | # Manual changes might be lost - proceed with caution! 3 | 4 | __metadata: 5 | version: 6 6 | cacheKey: 8 7 | 8 | "@matteusan/himig@workspace:.": 9 | version: 0.0.0-use.local 10 | resolution: "@matteusan/himig@workspace:." 11 | dependencies: 12 | "@matteusan/pintig": ^1.9 13 | "@matteusan/sentro": ^1.2.3 14 | languageName: unknown 15 | linkType: soft 16 | 17 | "@matteusan/pintig@npm:^1.9": 18 | version: 1.9.0 19 | resolution: "@matteusan/pintig@npm:1.9.0" 20 | dependencies: 21 | "@matteusan/sentro": ^1.2.3 22 | checksum: 9c9493bbb599cb870fa77d8097f78e20df924e51618f013e867c55787294efd91c653bd3a8dbd27d1b7ca3bcece3fa34104687cab93d1a962532320684bc2d46 23 | languageName: node 24 | linkType: hard 25 | 26 | "@matteusan/sentro@npm:^1.2.3": 27 | version: 1.2.3 28 | resolution: "@matteusan/sentro@npm:1.2.3" 29 | peerDependencies: 30 | sass: ^1.74.1 31 | checksum: 80460e4b1195f464a63ab96c7542fe9ec9ca920b2c18ade7f0960c1d1f6b3896e6466af4dffdc812f9a95c39dad463565a8056000d21accc7cf040b413b203f6 32 | languageName: node 33 | linkType: hard 34 | -------------------------------------------------------------------------------- /packages/himig-components/button/_button-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'accent-400', 8 | hover: 'accent-300', 9 | active: 'accent-200', 10 | focus: 'accent-400', 11 | disabled: 'disabled', 12 | ), 13 | ink: ( 14 | default: 'accent-ink', 15 | hover: 'accent-ink', 16 | active: 'accent-ink', 17 | focus: 'accent-ink', 18 | disabled: 'disabled-ink', 19 | ), 20 | border: ( 21 | default: 'accent-400', 22 | hover: 'accent-300', 23 | active: 'accent-200', 24 | focus: 'accent-400', 25 | disabled: 'disabled', 26 | ), 27 | ); 28 | 29 | $init-struct: ( 30 | width: ( 31 | default: 100%, 32 | min: 69px, 33 | max: max-content 34 | ), 35 | padding: ('md' 'lg'), 36 | radius: 'var(--ms-theme-radius-small)', 37 | border-width: 'xs', 38 | border-style: solid, 39 | gap: 'xs' 40 | ); 41 | 42 | $init-typography: ( 43 | family: 'var(--ms-theme-family-global)', 44 | size: 'var(--ms-theme-size-global)', 45 | weight: 'var(--ms-theme-weight-title)', 46 | line-height: 1.25rem 47 | ); 48 | 49 | $init-settings: (); -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/himig-feature-requests.yml: -------------------------------------------------------------------------------- 1 | name: Feature Requests 2 | description: Request a feature 3 | title: "[FEAT] " 4 | labels: ["feature request"] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Detailed description 10 | description: Provide a detailed description of the change or addition you are proposing 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: context 15 | attributes: 16 | label: Context 17 | description: | 18 | Why is this change important to you? How would you use it? 19 | How can it benefit other users? 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: possible-implementation 24 | attributes: 25 | label: Possible implementation 26 | description: Not obligatory, but suggest an idea for implementing addition or change 27 | validations: 28 | required: false 29 | - type: textarea 30 | id: extra-info 31 | attributes: 32 | label: Additional information 33 | description: Is there anything else we should know about this feature? 34 | validations: 35 | required: false -------------------------------------------------------------------------------- /packages/himig/ms-select-field/_select-field-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'surface-400', 8 | focus: 'accent-400', 9 | disabled: 'disabled', 10 | error: ( 11 | default: 'error-600', 12 | focus: 'error-600' 13 | ) 14 | ), 15 | ink: ( 16 | default: 'surface-ink', 17 | helper: 'surface-ink', 18 | disabled: 'disabled-ink', 19 | error: 'error-400' 20 | ), 21 | border: ( 22 | default: 'surface-600', 23 | focus: 'accent-400', 24 | disabled: 'disabled', 25 | error: ( 26 | default: 'error-600', 27 | focus: 'error-500' 28 | ) 29 | ), 30 | ); 31 | 32 | $init-struct: ( 33 | width: 100%, 34 | padding: ('md', 'lg'), 35 | radius: 'small', 36 | border-width: ( 37 | 'default': 'xs', 38 | 'focus': 'xs' 39 | ), 40 | border-style: solid, 41 | gap: 'sm' 42 | ); 43 | 44 | $init-typography: ( 45 | family: 'global', 46 | size: ( 47 | default: 'body', 48 | label: 'body', 49 | helper: 'small' 50 | ), 51 | weight: 'normal', 52 | line-height: 'normal' 53 | ); 54 | 55 | $init-settings: (); -------------------------------------------------------------------------------- /tests/sass/with-html.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/himig' with ($expressive-inks: true); 6 | @use 'pkg:@matteusan/himig/ms-badge'; 7 | @use 'pkg:@matteusan/himig/ms-button'; 8 | @use 'pkg:@matteusan/himig/ms-card'; 9 | @use 'pkg:@matteusan/himig/ms-form-field'; 10 | @use 'pkg:@matteusan/himig/ms-link'; 11 | @use 'pkg:@matteusan/himig/ms-list'; 12 | @use 'pkg:@matteusan/himig/ms-select-field'; 13 | @use 'pkg:@matteusan/himig/ms-tick-field'; 14 | @use 'pkg:@matteusan/himig/ms-utils'; 15 | 16 | @include himig.init( 17 | $theme: ( 18 | // background: 'ms-neutral.200', 19 | 'extend': ( 20 | 'bruh': ( 21 | 'lol': 0.5rem 22 | ) 23 | ) 24 | ), 25 | ) { 26 | @include ms-badge.render(); 27 | @include ms-button.render(); 28 | @include ms-card.render(); 29 | @include ms-form-field.render(); 30 | @include ms-link.render(); 31 | @include ms-list.render(); 32 | @include ms-select-field.render(); 33 | @include ms-tick-field.render(); 34 | 35 | @include ms-utils.layout(); 36 | @include ms-utils.color(); 37 | @include ms-utils.struct(); 38 | @include ms-utils.typography(); 39 | } -------------------------------------------------------------------------------- /specs/foundation/structure/layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Layout 3 | slug: /foundation/structure/layout 4 | --- 5 | # Layout 6 | Layout defines the static behavior of an element in an interface. Himig uses layout to organize content in an interface. 7 | 8 | ## Flow 9 | By default, Himig uses a flex-box column-flowing layout scheme. However, you can turn on 'grid mode' by changing a property inside the main content wrapper. 10 | 11 | ### Grid mode 12 | To turn on grid mode, add this line in your Himig init content block: 13 | 14 | ```scss 15 | @include himig.key-bind('content-wrap-display', grid); 16 | ``` 17 | 18 | In grid mode, Himig uses the 12-column grid system to cover all use cases for layout on multiple devices. 19 | 20 | Read more about this layout system [here](https://m2.material.io/design/layout/responsive-layout-grid.html). 21 | 22 | | Breakpoint | # of columns | 23 | |-----------------|--------------| 24 | | `breakpoint.sm` | 4 | 25 | | `breakpoint.md` | 8 | 26 | | `breakpoint.lg` | 12 | 27 | 28 | ## Implementing Layout 29 | We found that using utility classes to compose layouts is the way to go. Hence, we've not provided any form of mixin to 30 | let you compose layouts. -------------------------------------------------------------------------------- /packages/himig/ms-card/_card-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'primary-600' 8 | ), 9 | ink: ( 10 | default: 'primary-ink', 11 | subtitle: 'primary-ink', 12 | ), 13 | border: 'primary-600', 14 | ); 15 | 16 | $init-struct: ( 17 | width: ( 18 | default: 100%, 19 | min: auto, 20 | max: auto 21 | ), 22 | radius: 'md', 23 | padding: 'lg', 24 | gap: ( 25 | section: 'md', 26 | header: 'md', 27 | footer: 'md', 28 | ) 29 | ); 30 | 31 | $init-typography: ( 32 | family: ( 33 | 'default': 'body', 34 | 'title': 'title', 35 | 'subtitle': 'body', 36 | ), 37 | size: ( 38 | 'default': 'body', 39 | 'title': 'subtitle', 40 | 'subtitle': 'body', 41 | 'icon': 66px 42 | ), 43 | weight: ( 44 | 'default': 'normal', 45 | 'title': 'normal', 46 | 'subtitle': 'normal', 47 | ), 48 | line-height: ( 49 | 'default': 'normal', 50 | 'title': 'x-short', 51 | 'subtitle': 'normal', 52 | ), 53 | squeeze: ( 54 | 'default': 'normal', 55 | 'title': 'tight', 56 | 'subtitle': 'normal', 57 | ), 58 | ); 59 | 60 | $init-settings: (); -------------------------------------------------------------------------------- /packages/himig/ms-form-field/_form-field-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: 'surface-400', 8 | focus: 'accent-400', 9 | disabled: 'disabled', 10 | error: ( 11 | default: 'error-600', 12 | focus: 'error-600' 13 | ) 14 | ), 15 | ink: ( 16 | default: 'surface-ink', 17 | helper: 'surface-ink', 18 | disabled: 'disabled-ink', 19 | error: 'error-400' 20 | ), 21 | border: ( 22 | default: 'surface-600', 23 | focus: 'accent-400', 24 | disabled: 'disabled', 25 | error: ( 26 | default: 'error-600', 27 | focus: 'error-500' 28 | ) 29 | ), 30 | ); 31 | 32 | $init-struct: ( 33 | width: ( 34 | default: 100%, 35 | min: auto, 36 | max: auto 37 | ), 38 | padding: ('md', 'lg'), 39 | radius: 'small', 40 | border-width: ( 41 | 'default': 'xs', 42 | 'focus': 'xs' 43 | ), 44 | border-style: solid, 45 | gap: 'sm' 46 | ); 47 | 48 | $init-typography: ( 49 | family: 'global', 50 | size: ( 51 | default: 'body', 52 | label: 'body', 53 | helper: 'small' 54 | ), 55 | weight: 'normal', 56 | line-height: 'normal' 57 | ); 58 | 59 | $init-settings: (); -------------------------------------------------------------------------------- /packages/himig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@matteusan/himig", 3 | "version": "1.9.0", 4 | "description": "Matteu's personal design system for building user interfaces.", 5 | "repository": "https://github.com/MatteuSan/himig", 6 | "author": "Matteu ", 7 | "license": "MIT", 8 | "publishConfig": { 9 | "access": "public" 10 | }, 11 | "exports": { 12 | ".": { 13 | "sass": "./_index.scss" 14 | }, 15 | "./ms-badge": { 16 | "sass": "./ms-badge/_index.scss" 17 | }, 18 | "./ms-button": { 19 | "sass": "./ms-button/_index.scss" 20 | }, 21 | "./ms-card": { 22 | "sass": "./ms-card/_index.scss" 23 | }, 24 | "./ms-form-field": { 25 | "sass": "./ms-form-field/_index.scss" 26 | }, 27 | "./ms-link": { 28 | "sass": "./ms-link/_index.scss" 29 | }, 30 | "./ms-list": { 31 | "sass": "./ms-list/_index.scss" 32 | }, 33 | "./ms-select-field": { 34 | "sass": "./ms-select-field/_index.scss" 35 | }, 36 | "./ms-tick-field": { 37 | "sass": "./ms-tick-field/_index.scss" 38 | }, 39 | "./ms-utils": { 40 | "sass": "./ms-utils/_index.scss" 41 | } 42 | }, 43 | "peerDependencies": { 44 | "@matteusan/pintig": "^1.10" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/pintig/color/_color-utils.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | 7 | /// Mixin for applying background colors. 8 | /// @param {string} $component 9 | /// @param {token|color} $color 10 | /// @param {string} $intent 11 | /// @return {void} background color styles. 12 | @mixin fill($component, $color, $intent: 'create') { 13 | @include theme.property( 14 | background, 15 | ('#{$component}-fill', $color), 16 | $intent 17 | ); 18 | } 19 | 20 | /// Mixin for applying text colors. 21 | /// @param {string} $component 22 | /// @param {token|color} $color 23 | /// @param {string} $intent 24 | /// @return {void} text color styles. 25 | @mixin ink($component, $color, $intent: 'create') { 26 | @include theme.property( 27 | color, 28 | ('#{$component}-ink', $color), 29 | $intent 30 | ); 31 | } 32 | 33 | /// Mixin for applying border colors. 34 | /// @param {string} $component 35 | /// @param {token|color} $color 36 | /// @param {string} $intent 37 | /// @return {void} border color styles. 38 | @mixin border($component, $color, $intent: 'create') { 39 | @include theme.property( 40 | border-color, 41 | ('#{$component}-border', $color), 42 | $intent 43 | ); 44 | } -------------------------------------------------------------------------------- /packages/himig/ms-utils/_util-helpers.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:list'; 7 | 8 | @function handle-include-exclude($default-utils, $include: (), $exclude: ()) { 9 | $resolved-utils: $default-utils; 10 | 11 | @if not pintig.is-empty($include) and not pintig.is-empty($exclude) { 12 | @error 'Cannot use $include and $exclude at the same time. Please only use one or the other.'; 13 | } 14 | 15 | @if not pintig.is-empty($include) { 16 | @each $included in $include { 17 | @if not list.index($default-utils, $included) { 18 | @error 'Invalid utility key: #{$included}. Expecting a valid util key: #{$default-utils}.'; 19 | } 20 | } 21 | $resolved-utils: $include; 22 | @return $resolved-utils; 23 | } 24 | 25 | @if not pintig.is-empty($exclude) { 26 | @each $excluded in $exclude { 27 | @if not list.index($default-utils, $excluded) { 28 | @error 'Invalid utility key: #{$excluded}. Expecting a valid util key: #{$default-utils}.'; 29 | } 30 | $resolved-utils: list.set-nth($resolved-utils, list.index($resolved-utils, $excluded), null); 31 | @return $resolved-utils; 32 | } 33 | } 34 | 35 | @return $resolved-utils; 36 | } -------------------------------------------------------------------------------- /packages/himig-components/form-field/ms-form-field.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { html, LitElement, TemplateResult } from 'lit'; 8 | import { customElement } from 'lit/decorators.js'; 9 | 10 | @customElement('ms-form-field') 11 | export default class MSFormField extends LitElement { 12 | label: string; 13 | name: string 14 | type: string; 15 | placeholder?: string; 16 | helper?: string; 17 | value?: string; 18 | isDisabled?: boolean = false; 19 | isRequired?: boolean = false; 20 | isReadonly?: boolean = false; 21 | 22 | constructor() { 23 | super(); 24 | this.placeholder = this.label; 25 | } 26 | 27 | override render(): TemplateResult<1> { 28 | return html` 29 | 42 | `; 43 | }; 44 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/himig-bug-reports.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[BUG] " 4 | labels: ["bug"] 5 | body: 6 | - type: textarea 7 | id: problem-details 8 | attributes: 9 | label: What happened? 10 | description: Also tell us, what did you expect to happen? 11 | placeholder: Tell us what you see! 12 | value: "A bug happened!" 13 | validations: 14 | required: true 15 | - type: markdown 16 | attributes: 17 | value: | 18 | Attach some images if applicable. 19 | - type: dropdown 20 | id: version 21 | attributes: 22 | label: Version 23 | description: What version of Himig are you using? 24 | options: 25 | - 0.x 26 | validations: 27 | required: true 28 | - type: input 29 | id: browser 30 | attributes: 31 | label: What browser are you experiencing the problem on? 32 | - type: input 33 | id: browser-version 34 | attributes: 35 | label: What browser are you experiencing the problem on? 36 | - type: textarea 37 | id: logs 38 | attributes: 39 | label: Relevant log output 40 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 41 | render: shell -------------------------------------------------------------------------------- /packages/himig/ms-list/_list-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | .ms-list { 9 | display: flex; 10 | flex-flow: column nowrap; 11 | overflow: hidden; 12 | 13 | @content; 14 | 15 | .ms-list__item { 16 | display: flex; 17 | flex-flow: row nowrap; 18 | align-items: center; 19 | list-style: none; 20 | @include pintig.color-apply('list-item', ( 21 | fill: rgba(0 0 0 / 0), 22 | ink: currentColor, 23 | border: rgba(0 0 0 / 0), 24 | )); 25 | @include pintig.struct-apply('list-item', ( 26 | width: ( 27 | default: 100%, 28 | min: auto, 29 | max: auto 30 | ), 31 | padding: 0, 32 | border-width: 'xs', 33 | border-style: 'solid', 34 | gap: 0 35 | )); 36 | @include pintig.typography-apply('list-item', ( 37 | family: inherit, 38 | size: inherit, 39 | weight: inherit, 40 | line-height: inherit, 41 | )); 42 | transition-property: background, color, border; 43 | transition-duration: pintig.token-get('time-short'); 44 | transition-timing-function: pintig.token-get('easing'); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /packages/pintig/component/_component-extend.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:list'; 6 | @use 'sass:string'; 7 | @use '../tools'; 8 | 9 | @mixin extend($component-name, $extension) { 10 | $_extensions: tools.str-split($extension, '.'); 11 | $_sanitized-extension: ''; 12 | 13 | @each $ext in $_extensions { 14 | @if list.index($_extensions, $ext) == 1 { 15 | @if string.slice($ext, 1, 3) == 'is-' { 16 | $_sanitized-extension: $_sanitized-extension + string.slice($ext, 4); 17 | } @else { 18 | $_sanitized-extension: $_sanitized-extension + $ext; 19 | } 20 | } @else if list.index($_extensions, $ext) == list.length($_extensions) { 21 | @if string.slice($ext, 1, 3) == 'is-' { 22 | $_sanitized-extension: $_sanitized-extension + '.#{$ext}'; 23 | } @else { 24 | $_sanitized-extension: $_sanitized-extension + '.is-#{$ext}'; 25 | } 26 | } @else { 27 | @if string.slice($ext, 1, 3) == 'is-' { 28 | $_sanitized-extension: $_sanitized-extension + '.#{$ext}'; 29 | } @else { 30 | $_sanitized-extension: $_sanitized-extension + '.is-#{$ext}'; 31 | } 32 | } 33 | } 34 | 35 | .ms-#{$component-name}.is-#{$_sanitized-extension} { 36 | @content; 37 | } 38 | } -------------------------------------------------------------------------------- /scripts/scss-to-lit.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import * as fs from 'node:fs'; 8 | import * as path from 'node:path'; 9 | 10 | /** 11 | * @param {string} startPath 12 | * @param {RegExp} filter 13 | * @param {(filename: string) => void} callback 14 | * @returns {void} 15 | */ 16 | function fromDir(startPath: string, filter: RegExp, callback: (filename: string) => void): void { 17 | if (!fs.existsSync(startPath)) { 18 | console.log("no dir ", startPath); 19 | return; 20 | } 21 | 22 | const files = fs.readdirSync(startPath); 23 | files.forEach((file: string) => { 24 | const filename = path.join(startPath, file); 25 | const stat = fs.lstatSync(filename); 26 | if (stat.isDirectory()) { 27 | fromDir(filename, filter, callback); 28 | } else if (filter.test(filename)) callback(filename); 29 | }); 30 | } 31 | 32 | fromDir("packages/himig-components", /\.css/, (filename) => { 33 | const cssContent = fs.readFileSync(filename, {encoding: 'utf-8'}).replace(/\/\*#\ sourceMappingURL=[^\*]+ \*\//, ''); 34 | const tsFile = filename.replace('.css', '.ts'); 35 | fs.writeFileSync(tsFile, `/** 36 | * @license 37 | * Copyright ${ new Date().getFullYear() } (c) MatteuSan 38 | * SPDX-License-Identifier: MIT 39 | */ 40 | import { css } from 'lit'; 41 | export const styles = css\`${cssContent}\`; 42 | `); 43 | }); -------------------------------------------------------------------------------- /packages/pintig/theme/_theme-properties.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../tokens' as primitive-token; 6 | @use '../tools'; 7 | @use 'sass:map'; 8 | @use 'sass:list'; 9 | @use 'sass:string'; 10 | 11 | @use 'theme-tokens' as token; 12 | @use 'theme-keys' as key; 13 | 14 | $_valid-intents: ( 15 | 'create', 16 | 'bind' 17 | ); 18 | 19 | @mixin property($property, $values, $intent: 'create') { 20 | $key: list.nth($values, 1); 21 | $value: list.nth($values, 2); 22 | 23 | @if not list.index($_valid-intents, $intent) { 24 | @error 'Invalid intent: #{$intent}. Expecting a valid intent: #{$_valid-intents}.'; 25 | } 26 | 27 | @if $intent == 'bind' { 28 | @include key.bind($key, _assert-value($value)); 29 | } @else if $intent == 'create' { 30 | #{$property}: key.create($key, _assert-value($value)); 31 | } 32 | } 33 | 34 | @function _assert-value($query) { 35 | @if tools.is-type($query, 'list', false) { 36 | @if list.length($query) == 2 and 37 | token.check(list.nth($query, 1)) or primitive-token.primitive-token-check(list.nth($query, 1)) and 38 | list.nth($query, 2) == string.unquote('!important') { 39 | @return token.switch(list.nth($query, 1), primitive-token.primitive-token-switch(list.nth($query, 1))) + ' ' + list.nth($query, 2); 40 | } 41 | } 42 | @return token.switch($query, primitive-token.primitive-token-switch($query)); 43 | } -------------------------------------------------------------------------------- /specs/api/constants.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Constants 3 | slug: /api/constants 4 | --- 5 | # Constants 6 | These are exported constants available for consumption. You can use them directly using the `himig` import namespace. 7 | 8 | ## Color Constants 9 | ```scss 10 | $fill-default-opacity: 0%; 11 | $fill-hover-opacity: 20%; 12 | $fill-focus-opacity: 20%; 13 | $fill-active-opacity: 33%; 14 | 15 | $border-default-opacity: 0%; 16 | $border-hover-opacity: 2%; 17 | $border-focus-opacity: 2%; 18 | $border-active-opacity: 3.3%; 19 | 20 | $outline-focus-opacity: 50%; 21 | 22 | /// @type map 23 | /// @description helper variable for inheriting values for all color properties 24 | $color-inherit-template: ( 25 | fill: inherit, 26 | ink: inherit, 27 | border: inherit 28 | ); 29 | ``` 30 | 31 | ## Struct Constants 32 | ```scss 33 | /// @type map 34 | /// @description helper variable for inheriting values for all struct properties 35 | $struct-inherit-template: ( 36 | width: inherit, 37 | height: inherit, 38 | radius: inherit, 39 | padding: inherit, 40 | margin: inherit, 41 | gap: inherit, 42 | border-width: inherit, 43 | border-style: inherit, 44 | shadow: inherit 45 | ); 46 | ``` 47 | 48 | ## Typography Constants 49 | ```scss 50 | /// @type map 51 | /// @description helper variable for inheriting values for all typography properties 52 | $type-inherit-template: ( 53 | family: inherit, 54 | size: inherit, 55 | weight: inherit, 56 | line-height: inherit 57 | ); 58 | ``` -------------------------------------------------------------------------------- /packages/himig/ms-tick-field/_tick-field-theme.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $init-color: ( 6 | fill: ( 7 | default: rgba(0 0 0 / 0), 8 | hover: 'accent-400', 9 | checked: 'accent-400', 10 | disabled: 'disabled', 11 | input: ( 12 | default: rgba(0 0 0 / 0), 13 | hover: rgba(0 0 0 / 0), 14 | checked: 'accent-400', 15 | disabled: 'disabled', 16 | ), 17 | ), 18 | ink: ( 19 | default: 'surface-ink', 20 | input: 'accent-ink', 21 | disabled: 'disabled-ink', 22 | ), 23 | border: ( 24 | default: 'surface-600', 25 | hover: 'accent-400', 26 | checked: 'accent-400', 27 | disabled: 'disabled', 28 | input: ( 29 | default: 'surface-600', 30 | hover: 'accent-400', 31 | checked: 'accent-400', 32 | disabled: 'disabled', 33 | ) 34 | ), 35 | ); 36 | 37 | $init-struct: ( 38 | width: ( 39 | default: 100%, 40 | min: auto, 41 | max: auto 42 | ), 43 | radius: ( 44 | default: 'sm', 45 | input: ( 46 | checkbox: 'xs', 47 | radio: 'pill' 48 | ) 49 | ), 50 | padding: ('lg'), 51 | gap: ( 52 | default: 0.7rem, 53 | text: 0.3rem 54 | ), 55 | border-width: 'xs', 56 | border-style: 'solid' 57 | ); 58 | 59 | $init-typography: ( 60 | family: 'global', 61 | size: ( 62 | default: 'global', 63 | helper: 'small' 64 | ), 65 | weight: 'global', 66 | line-height: 'global', 67 | ); 68 | 69 | $init-settings: (); -------------------------------------------------------------------------------- /media/gds-icon.svg: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /specs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | slug: / 4 | --- 5 | # Introduction 6 | Himig (formerly Himig Web) is Matteu's design system for building web-based products and projects. It 7 | helps me build products faster, make it more accessible, and deliver quality and unforgettable experiences. 8 | 9 | ## Goals 10 | Aside from being device-agnostic, beautiful/aesthetic, and accessible, we've also had a few more goals in mind: 11 | 12 | ### Consistency 13 | One of our goals that we want to achieve in this design system is consistency in terms of design and in user experience. 14 | The design system should be able to deliver consistent 15 | experiences throughout GrowStocks' wide range of digital products, regardless of any project-specific design problems it 16 | needs to solve. 17 | 18 | ### Development Speed 19 | When we build our products, it is important for us to not reinvent the wheel. If a tool is at your disposal, use it as 20 | much as you can before you reach for something else. The design system should be able to provide excellent tooling for creating 21 | interfaces in a speedy manner, this way developers won't have to exert effort into recreating components, patterns, 22 | practices, and principles we've already made and agreed on. 23 | 24 | ### Framework Agnostic 25 | Whether you're rendering HTML from the server using server-side code, or you're rendering client-side interfaces using Javascript, the design system should be able to cater to different tech stacks. 26 | 27 | Now, are you ready to start working with Himig? Click [here](getting-started/setup.md) to get started! -------------------------------------------------------------------------------- /tests/sass/pintig/theme-tokens.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | :root { 9 | @include pintig.token-config( 10 | $surface: ( 11 | 200: #8bf, 12 | 400: #07f 13 | ), 14 | $accent: ( 15 | 200: #fb8, 16 | 400: #f70 17 | ), 18 | ); 19 | } 20 | 21 | @include true.describe('Theme Tokens [module]') { 22 | @include true.it('should return a theme token custom property') { 23 | $test: pintig.token-get('accent-400'); 24 | $test2: pintig.token-get('surface-200'); 25 | @include true.assert-equal($test, var(--ms-accent-400)); 26 | @include true.assert-equal($test2, var(--ms-surface-200)); 27 | } 28 | @include true.it('should return a theme token raw value') { 29 | $test: pintig.token-get-raw('accent-400'); 30 | $test2: pintig.token-get-raw('surface-200'); 31 | @include true.assert-equal($test, #f70); 32 | @include true.assert-equal($test2, #8bf); 33 | } 34 | @include true.it('should switch to itself when not valid') { 35 | $test: pintig.token-switch('accent-400'); 36 | $test2: pintig.token-switch(inherit); 37 | @include true.assert-equal($test, var(--ms-accent-400)); 38 | @include true.assert-equal($test2, inherit); 39 | } 40 | @include true.it('should switch to itself when not valid; when valid, it uses the raw value') { 41 | $test: pintig.token-switch-raw('accent-400'); 42 | $test2: pintig.token-switch-raw(inherit); 43 | @include true.assert-equal($test, #f70); 44 | @include true.assert-equal($test2, inherit); 45 | } 46 | } 47 | 48 | @include true.report(); -------------------------------------------------------------------------------- /packages/pintig/tools/_tools-validation.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'sass:list'; 6 | @use 'sass:meta'; 7 | @use 'sass:string'; 8 | 9 | /// Public function for data type validation. 10 | /// @param {*} $query 11 | /// @param {string} $reference 12 | /// @param {boolean} $errors 13 | /// @return {boolean|error} true if matching, error/false if not. 14 | @function is-type($query, $reference, $errors: true, $custom-error: null) { 15 | @if $errors != true { 16 | @return _validate-data-type($reference, $query); 17 | } @else { 18 | @if _validate-data-type($reference, $query) == false { 19 | @if not is-empty($custom-error) { 20 | @error $custom-error; 21 | } 22 | @error 'Invalid #{string.unquote($reference)}: #{$query}! Expecting a valid #{string.unquote($reference)}.'; 23 | } @else { 24 | @return true; 25 | } 26 | } 27 | } 28 | 29 | /// Checks if query is empty. 30 | /// @param {*} $query 31 | /// @return {boolean} true if empty, false if not. 32 | @function is-empty($query) { 33 | @if not $query 34 | or $query == '' 35 | or $query == () 36 | or $query == none 37 | or $query == null 38 | or list.length($query) == 0 { 39 | @return true; 40 | } 41 | @return false; 42 | } 43 | 44 | /// Validates a data type on a given reference and query. 45 | /// @access private 46 | /// @param {string} $reference 47 | /// @param {*} $query 48 | /// @return {boolean} true if matching, false if not. 49 | @function _validate-data-type($reference, $query) { 50 | @if meta.type-of($query) != string.unquote($reference) { 51 | @return false; 52 | } 53 | @return true; 54 | } -------------------------------------------------------------------------------- /packages/pintig/color/_color-setters.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | @use '../tools'; 7 | @use 'sass:map'; 8 | @use 'sass:list'; 9 | 10 | @use 'color-utils' as util; 11 | @use 'color-validators' as validator; 12 | 13 | @mixin _base($component, $theme, $settings: ()) { 14 | $_init-settings: (intent: 'create'); 15 | $_settings: map.merge($_init-settings, $settings); 16 | 17 | /// @type string 18 | $intent: map.get($_settings, 'intent'); 19 | 20 | $fill: theme.theme-get($theme, 'fill'); 21 | $ink: theme.theme-get($theme, 'ink'); 22 | $border: theme.theme-get($theme, 'border'); 23 | 24 | @if not tools.is-type($theme, 'map') { 25 | @error '$theme is expecting a map. Map was not given.'; 26 | } @else { 27 | @include validator.validate-color-props(map.keys($theme)) { 28 | @if $fill { 29 | @include util.fill($component, $fill, $intent); 30 | } 31 | @if $ink { 32 | @include util.ink($component, $ink, $intent); 33 | } 34 | @if $border { 35 | @include util.border($component, $border, $intent); 36 | } 37 | } 38 | } 39 | } 40 | 41 | /// Mixin that applies color styles to a component. 42 | /// @param {string} $component 43 | /// @param {map} $theme 44 | /// @return {void} void. 45 | @mixin apply($component, $theme) { 46 | @include _base($component, $theme); 47 | } 48 | 49 | /// Mixin that binds color styles to a component with existing keys. 50 | /// @param {string} $component 51 | /// @param {map} $theme 52 | /// @return {void} void. 53 | @mixin bind($component, $theme) { 54 | @include _base($component, $theme, ( 55 | 'intent': bind 56 | )); 57 | } -------------------------------------------------------------------------------- /packages/pintig/_index.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | $prefix: 'ms' !default; 6 | $context: '' !default; 7 | $module-mode: false !default; 8 | $test-mode: $module-mode !default; 9 | $vue-mode: $test-mode !default; 10 | $validation: if($vue-mode == true, false, true); 11 | $expressive-inks: false !default; 12 | 13 | /// @type map 14 | /// @description helper variable for inheriting values for all color properties 15 | $color-inherit-template: ( 16 | fill: inherit, 17 | ink: inherit, 18 | border: inherit 19 | ); 20 | 21 | /// @type map 22 | /// @description helper variable for inheriting values for all struct properties 23 | $struct-inherit-template: ( 24 | width: inherit, 25 | height: inherit, 26 | radius: inherit, 27 | padding: inherit, 28 | margin: inherit, 29 | gap: inherit, 30 | border-width: inherit, 31 | border-style: inherit, 32 | shadow: inherit 33 | ); 34 | 35 | /// @type map 36 | /// @description helper variable for inheriting values for all typography properties 37 | $type-inherit-template: ( 38 | family: inherit, 39 | size: inherit, 40 | weight: inherit, 41 | line-height: inherit, 42 | squeeze: inherit, 43 | stretch: inherit, 44 | ); 45 | 46 | @use 'pkg:@matteusan/sentro' with ( 47 | $prefix: $prefix, 48 | $context: $context, 49 | $token-validation: $validation, 50 | $key-validation: $validation, 51 | $breakpoint-validation: $validation, 52 | ); 53 | 54 | @forward 'tokens'; 55 | @forward 'theme' with ($expressive-inks: $expressive-inks); 56 | @forward 'tools'; 57 | @forward 'breakpoint' as breakpoint-*; 58 | 59 | @forward 'color' as color-*; 60 | @forward 'struct' as struct-*; 61 | @forward 'sprite' as sprite-*; 62 | @forward 'typography' as typography-*; 63 | @forward 'component' as component-*; -------------------------------------------------------------------------------- /packages/himig/ms-list/_list-styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'list-base' as base; 7 | @use 'list-theme' as theme; 8 | 9 | @mixin render( 10 | $color: (), 11 | $struct: (), 12 | $typography: (), 13 | $settings: () 14 | ) { 15 | $color: pintig.theme-merge(theme.$init-color, $color); 16 | $struct: pintig.theme-merge(theme.$init-struct, $struct); 17 | $typography: pintig.theme-merge(theme.$init-typography, $typography); 18 | $settings: pintig.theme-merge(theme.$init-settings, $settings); 19 | 20 | @layer components { 21 | @include base.init() { 22 | @include pintig.color-bind('list-item', $color); 23 | @include pintig.struct-apply('list', ( 24 | radius: pintig.theme-get($struct, 'radius'), 25 | )); 26 | @include pintig.struct-bind('list-item', $struct); 27 | @include pintig.typography-bind('list-item', $typography); 28 | 29 | &:where(.is-selectable) { 30 | .ms-list__item { 31 | cursor: pointer; 32 | 33 | &:hover { 34 | @include pintig.color-bind('list-item', ( 35 | fill: pintig.theme-get($color, 'fill', 'hover'), 36 | border: pintig.theme-get($color, 'border', 'hover') 37 | )); 38 | } 39 | 40 | &:focus { 41 | @include pintig.color-bind('list-item', ( 42 | fill: pintig.theme-get($color, 'fill', 'focus'), 43 | border: pintig.theme-get($color, 'border', 'focus') 44 | )); 45 | } 46 | 47 | &:active { 48 | @include pintig.color-bind('list-item', ( 49 | fill: pintig.theme-get($color, 'fill', 'active'), 50 | border: pintig.theme-get($color, 'border', 'active') 51 | )); 52 | } 53 | } 54 | } 55 | 56 | &:where(.is-raised) { 57 | @include pintig.struct-shadow('list', 'xs'); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /packages/himig/ms-button/_button-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | .ms-button { 9 | display: inline-flex; 10 | flex-flow: row nowrap; 11 | justify-content: center; 12 | align-items: center; 13 | text-align: center; 14 | 15 | user-select: none; 16 | overflow: visible; 17 | vertical-align: middle; 18 | 19 | cursor: pointer; 20 | outline: pintig.key-create('button-outline', none); 21 | 22 | -webkit-appearance: caret; 23 | transition-property: background, color, border; 24 | transition-duration: pintig.token-get('time-short'); 25 | transition-timing-function: pintig.token-get('easing'); 26 | 27 | @content; 28 | 29 | .ms-button__icon { 30 | position: relative; 31 | @include pintig.color-ink('button-icon', inherit); 32 | @include pintig.typography-size('button-icon', 'size.md'); 33 | /* @fallback */ 34 | width: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 35 | max-width: max-content; 36 | height: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 37 | 38 | > * { 39 | position: relative; 40 | @include pintig.color-ink('button-icon', inherit); 41 | @include pintig.typography-size('button-icon', 'size.md'); 42 | /* @fallback */ 43 | width: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 44 | max-width: max-content; 45 | height: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 46 | } 47 | } 48 | 49 | .ms-button__label { 50 | @include pintig.color-ink('button-label', inherit); 51 | @include pintig.typography-apply('button-label', ( 52 | family: inherit, 53 | size: inherit, 54 | weight: inherit, 55 | line-height: inherit 56 | )); 57 | text-align: center; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /packages/pintig/component/component-base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { MSComponent } from './types/ms-component'; 8 | import { accessProperty } from './component-properties'; 9 | 10 | export class MSComponentBase { 11 | 12 | protected GS_TARGET_COMPONENT: Element | HTMLElement | null; 13 | protected GS_REFERENCE_COMPONENT: MSComponent; 14 | protected GS_CUSTOM_PROPERTIES: string[]; 15 | private readonly GS_COMPONENT_NAME: string; 16 | 17 | constructor( 18 | targetComponent: Element | HTMLElement | null, 19 | referenceComponent: MSComponent, 20 | customProperties: string[], 21 | ) { 22 | this.GS_TARGET_COMPONENT = targetComponent; 23 | this.GS_REFERENCE_COMPONENT = referenceComponent; 24 | this.GS_CUSTOM_PROPERTIES = customProperties; 25 | this.GS_COMPONENT_NAME = this.GS_REFERENCE_COMPONENT.substring(3); 26 | } 27 | 28 | addClass(className: string): void { 29 | this.GS_TARGET_COMPONENT?.classList.add(className); 30 | } 31 | 32 | removeClass(className: string): void { 33 | this.GS_TARGET_COMPONENT?.classList.remove(className); 34 | } 35 | 36 | hasClass(className: string): boolean { 37 | return this.GS_TARGET_COMPONENT?.classList.contains(className) ?? false; 38 | } 39 | 40 | getClasses(): DOMTokenList|undefined { 41 | return this.GS_TARGET_COMPONENT?.classList; 42 | } 43 | 44 | getComponent(): Element|HTMLElement|null { 45 | return this.GS_TARGET_COMPONENT; 46 | } 47 | 48 | getReference(): MSComponent { 49 | return this.GS_REFERENCE_COMPONENT; 50 | } 51 | 52 | getCustomProperty(query: string): string { 53 | const prop: string = this.GS_CUSTOM_PROPERTIES[this.GS_CUSTOM_PROPERTIES.indexOf(query)]; 54 | return accessProperty(`${this.GS_COMPONENT_NAME}-${prop}`); 55 | } 56 | 57 | getCustomProperties(): string[] { 58 | return this.GS_CUSTOM_PROPERTIES; 59 | } 60 | 61 | getId(): string|undefined { 62 | return this.GS_TARGET_COMPONENT?.id; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /specs/foundation/structure/breakpoint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Breakpoint 3 | slug: /foundation/structure/breakpoint 4 | --- 5 | # Breakpoint 6 | Breakpoints are points on a screen that defines an element's style. Himig uses breakpoints to create fluid and responsive 7 | interfaces that embody its philosophies across multiple devices. 8 | 9 | ## Breakpoint Tokens 10 | Breakpoint tokens are constant, and cannot be mutated unless specified in the theme. These tokens are crafted to fit the Growstocks branding on multiple devices. 11 | 12 | | Token | Value | 13 | |-------------------|----------| 14 | | `breakpoint.none` | `0` | 15 | | `breakpoint.xs` | `277px` | 16 | | `breakpoint.sm` | `320px` | 17 | | `breakpoint.md` | `640px` | 18 | | `breakpoint.lg` | `890px` | 19 | | `breakpoint.xl` | `1077px` | 20 | 21 | ### Modifying the breakpoints 22 | Modifying the breakpoints are allowed if the product has a different design specification than the Growstocks brand. 23 | Using the `breakpoint-config()` mixin, you can modify these values by passing in a map of your breakpoints in the first 24 | parameter. 25 | 26 | By using this mixin, you can add/modify current values but can never delete the defaults. 27 | 28 | ```scss 29 | @use 'pkg:@matteusan/himig'; 30 | 31 | @include himig.breakpoint-config-override(( 32 | // Breakpoints... 33 | )); 34 | ``` 35 | 36 | If you want to completely reset the breakpoints, you can set the `$override` parameter to `true`. 37 | 38 | ```scss 39 | @use 'pkg:@matteusan/himig'; 40 | 41 | @include himig.breakpoint-config-override(( 42 | // Breakpoints... 43 | ), $override: true); 44 | ``` 45 | 46 | ## Applying Breakpoints 47 | To apply a breakpoint on an element, use the `breakpoint()` mixin. Query a [breakpoint token variant](#breakpoint-tokens) in the mixin and write 48 | content inside the declaration. 49 | 50 | ```scss 51 | @use 'pkg:@matteusan/himig'; 52 | 53 | .component { 54 | // Initial styles... 55 | 56 | @include himig.breakpoint-create('md') { 57 | // New styles... 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /packages/himig/ms-link/_link-styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'link-base' as base; 7 | @use 'link-theme' as theme; 8 | 9 | @mixin render( 10 | $color: (), 11 | $struct: (), 12 | $typography: (), 13 | $settings: () 14 | ) { 15 | $color: pintig.theme-merge(theme.$init-color, $color); 16 | $struct: pintig.theme-merge(theme.$init-struct, $struct); 17 | $typography: pintig.theme-merge(theme.$init-typography, $typography); 18 | $settings: pintig.theme-merge(theme.$init-settings, $settings); 19 | 20 | @layer components { 21 | @include base.init() { 22 | @include pintig.color-apply('link', ( 23 | fill: transparent, 24 | ink: pintig.theme-get($color, 'ink') 25 | )); 26 | @include pintig.typography-apply('link', ( 27 | family: pintig.theme-get($typography, 'family'), 28 | size: pintig.theme-get($typography, 'size'), 29 | weight: pintig.theme-get($typography, 'weight'), 30 | line-height: pintig.theme-get($typography, 'line-height') 31 | )); 32 | text-decoration: underline; 33 | transition-property: background, color, border; 34 | transition-duration: pintig.token-get('time-short'); 35 | transition-timing-function: pintig.token-get('easing'); 36 | 37 | &:hover { 38 | @include pintig.color-bind('link', ( 39 | fill: rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'hover')) r g b / pintig.$color-fill-hover-opacity) 40 | )); 41 | } 42 | 43 | &:focus { 44 | @include pintig.color-bind('link', ( 45 | fill: rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'focus')) r g b / pintig.$color-fill-focus-opacity) 46 | )); 47 | } 48 | 49 | &:active { 50 | @include pintig.color-bind('link', ( 51 | fill: rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'active')) r g b / pintig.$color-fill-active-opacity) 52 | )); 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /packages/pintig/tools/_tools-color.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'tools-validation' as validator; 6 | @use 'sass:color'; 7 | @use 'sass:map'; 8 | @use 'sass:math'; 9 | 10 | /// Tints a color from a given percentage. 11 | /// @param {color} $color 12 | /// @param {number} $percent 13 | /// @return {color} modified color. 14 | @function tint($color, $percent) { 15 | @return color.change( 16 | color.mix(#fff, $color, $percent, $method: color.space($color)), 17 | $space: color.space($color) 18 | ); 19 | } 20 | 21 | /// Shades a color from a given percentage. 22 | /// @param {color} $color 23 | /// @param {number} $percent 24 | /// @return {color} modified color. 25 | @function shade($color, $percent) { 26 | @return color.change( 27 | color.mix(#000, $color, $percent, $method: color.space($color)), 28 | $space: color.space($color), 29 | ); 30 | } 31 | 32 | @function _get-linear($color) { 33 | @if $color <= 0.4045 { 34 | @return math.div($color, 12.92); 35 | } @else { 36 | @return math.pow(math.div($color + 0.055, 1.055), 2.4); 37 | } 38 | } 39 | 40 | @function _get-luminance($color) { 41 | $r: _get-linear(math.div(color.channel($color, 'red', $space: rgb), 255)); 42 | $g: _get-linear(math.div(color.channel($color, 'green', $space: rgb), 255)); 43 | $b: _get-linear(math.div(color.channel($color, 'blue', $space: rgb), 255)); 44 | 45 | @return (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b); 46 | } 47 | 48 | /// Gets accessible ink from a given fill. 49 | /// @param {color} $fill 50 | /// @param {boolean} $expressive 51 | /// @return {color} accessible ink color. 52 | @function get-ink($fill, $expressive: false) { 53 | $_white-lumen: _get-luminance(#fff); 54 | $_expressiveness: 87.4%; 55 | 56 | @if $expressive { 57 | @if math.abs(_get-luminance($fill) - $_white-lumen) > 0.7 { 58 | @return tint($fill, $_expressiveness); 59 | } 60 | @return shade($fill, $_expressiveness); 61 | } 62 | 63 | @if math.abs(_get-luminance($fill) - $_white-lumen) > 0.7 { 64 | @return #fff; 65 | } 66 | @return #000; 67 | } -------------------------------------------------------------------------------- /tests/sass/pintig/theme-properties.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | :root { 9 | @include pintig.token-config($accent: ( 10 | 400: #f70 11 | )); 12 | } 13 | 14 | @include true.describe('Theme Property [function]') { 15 | @include true.it('should return a themed property') { 16 | @include true.assert { 17 | @include true.output { 18 | @include pintig.property(background, ( 19 | 'component-fill', pintig.primitive-token-get('ms-orange.400') 20 | )); 21 | } 22 | @include true.expect { 23 | background: var(--ms-component-fill, oklch(65% 0.2 47.5deg)); 24 | } 25 | } 26 | } 27 | 28 | @include true.it('should return a themed property binding') { 29 | @include true.assert { 30 | @include true.output { 31 | @include pintig.property(background, ( 32 | 'component-fill', pintig.primitive-token-get('ms-orange.400') 33 | ), $intent: 'bind'); 34 | } 35 | @include true.expect { 36 | --ms-component-fill: oklch(65% 0.2 47.5deg); 37 | } 38 | } 39 | } 40 | } 41 | 42 | @include true.describe('Theme Property (theme vs primitive) [function]') { 43 | @include true.it('should return a themed property') { 44 | @include true.assert { 45 | @include true.output { 46 | @include pintig.property(background, ( 47 | 'component-fill', 'ms-orange.400' 48 | )); 49 | } 50 | @include true.expect { 51 | background: var(--ms-component-fill, oklch(65% 0.2 47.5deg)); 52 | } 53 | } 54 | } 55 | 56 | @include true.it('should return a themed property binding') { 57 | @include true.assert { 58 | @include true.output { 59 | @include pintig.property(background-color, ( 60 | 'component-fill', 'accent-400' 61 | ), $intent: 'bind'); 62 | } 63 | @include true.expect { 64 | --ms-component-fill: var(--ms-accent-400); 65 | } 66 | } 67 | } 68 | } 69 | 70 | @include true.report(); -------------------------------------------------------------------------------- /packages/pintig/typography/_typography-setters.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | @use '../tools'; 7 | @use 'sass:map'; 8 | @use 'sass:list'; 9 | @use 'typography-utils' as util; 10 | @use 'typography-validators' as validator; 11 | 12 | /// Base mixin for apply() and bind() 13 | /// @access private 14 | @mixin _base($component, $theme, $settings: ()) { 15 | $_init-settings: ('intent': create); 16 | $_settings: map.merge($_init-settings, $settings); 17 | 18 | /// @type string 19 | $intent: map.get($_settings, 'intent'); 20 | 21 | $family: theme.theme-get($theme, 'family'); 22 | $size: theme.theme-get($theme, 'size'); 23 | $weight: theme.theme-get($theme, 'weight'); 24 | $line-height: theme.theme-get($theme, 'line-height'); 25 | $squeeze: theme.theme-get($theme, 'squeeze'); 26 | 27 | @if tools.is-type($theme, 'map', $custom-error: '$theme is expecting a map. Map was not given.') { 28 | @include validator.validate-typography-props(map.keys($theme)) { 29 | @if $family { 30 | @include util.family($component, $family, $intent); 31 | } 32 | @if $size { 33 | @include util.size($component, $size, $intent); 34 | } 35 | @if $weight { 36 | @include util.weight($component, $weight, $intent); 37 | } 38 | @if $line-height { 39 | @include util.line-height($component, $line-height, $intent); 40 | } 41 | @if $squeeze { 42 | @include util.squeeze($component, $squeeze, $intent); 43 | } 44 | } 45 | } 46 | } 47 | 48 | /// Mixin that applies typography styles to a component. 49 | /// @param {string} $component 50 | /// @param {map} $theme 51 | /// @return {void} void. 52 | @mixin apply($component, $theme) { 53 | @include _base($component, $theme); 54 | } 55 | 56 | /// Mixin that binds typography styles to a component with existing keys. 57 | /// @param {string} $component 58 | /// @param {map} $theme 59 | /// @return {void} void. 60 | @mixin bind($component, $theme) { 61 | @include _base($component, $theme, ( 62 | 'intent': bind 63 | )); 64 | } -------------------------------------------------------------------------------- /packages/himig-components/button/_button-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | :host .ms-button { 9 | display: inline-flex; 10 | flex-flow: row nowrap; 11 | justify-content: center; 12 | align-items: center; 13 | text-align: center; 14 | user-select: none; 15 | overflow: visible; 16 | vertical-align: middle; 17 | 18 | text-decoration: none; 19 | 20 | cursor: pointer; 21 | outline: pintig.key-create('button-outline', none); 22 | 23 | -webkit-appearance: caret; 24 | transition-property: background, color, border; 25 | transition-duration: pintig.token-get('time-short'); 26 | transition-timing-function: pintig.token-get('easing'); 27 | 28 | @content; 29 | 30 | .icon { 31 | position: relative; 32 | @include pintig.color-ink('button-icon', inherit); 33 | @include pintig.typography-size('button-icon', 'size.md'); 34 | /* @fallback */ 35 | width: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 36 | max-width: max-content; 37 | height: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 38 | 39 | &:empty { 40 | display: none; 41 | } 42 | 43 | ::slotted(*) { 44 | position: relative; 45 | @include pintig.color-ink('button-icon', inherit); 46 | @include pintig.typography-size('button-icon', 'size.md'); 47 | /* @fallback */ 48 | width: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 49 | max-width: max-content; 50 | height: pintig.key-create('button-icon-size', pintig.primitive-token-get('size.md')); 51 | } 52 | } 53 | 54 | .label { 55 | @include pintig.color-ink('button-label', inherit); 56 | @include pintig.typography-apply('button-label', ( 57 | family: inherit, 58 | size: inherit, 59 | weight: inherit, 60 | line-height: inherit 61 | )); 62 | text-align: center; 63 | 64 | &:empty { 65 | display: none; 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /specs/foundation/tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tokens 3 | slug: /foundation/tokens 4 | --- 5 | # Tokens 6 | Himig uses tokens as the foundation of its design. Tokens provide the smallest unit of design in an interface. 7 | 8 | ## Token-Driven Architecture 9 | > Token-driven architecture is a type of design system architecture that uses design tokens to run the designs. Design 10 | > tokens are singular, immutable, and powerful values/rules that govern over the design system.
11 | > 12 | > **- [Dev Protocol](https://hashi-docs.netlify.app/docs/develop/concepts#token-driven-architecture)** 13 | 14 | The token-driven architecture allows us to power the design language to create a more consistent feel in the design 15 | system, and in turn allow our developers (and you!) to build user experiences with confidence. 16 | 17 | ## Types of tokens 18 | Himig has two types of tokens, primitive tokens, and theme tokens. 19 | 20 | ### Primitive tokens 21 | Primitive tokens have no theme value attached to them, and are generally used as 'for-value' only. These include the 22 | pre-made color tokens, struct tokens, typography tokens, etc. These tokens cannot be accessed anywhere in the design 23 | system apart from using it in global theming and component theming. 24 | 25 | ### Theme tokens 26 | Theme tokens are the tokens that are most used in the design system, these token can be accessed all throughout the 27 | design system. These drive the entire design system. Theme tokens are generally more organized. Theme tokens can take 28 | both primitive token values, and user provided 29 | values. 30 | 31 | ## Token Syntax 32 | It is important to learn the token syntax to help you make sense of its nature when querying. 33 | 34 | All tokens are in `snake-case` and **CANNOT** be in `camelCase`. 35 | 36 | ### Primitive token syntax 37 | Primitive tokens have this same exact syntax: 38 | ```txt 39 | [key].[variant] 40 | ``` 41 | 42 | For example: 43 | ```txt 44 | ms-red.400 45 | ms-white.200 46 | ``` 47 | 48 | ### Theme token syntax 49 | Theme tokens have a different syntax than the previous token type. 50 | ```txt 51 | [key]-[variant] 52 | ``` 53 | 54 | For example: 55 | ```txt 56 | primary-400 57 | accent-200 58 | surface-ink 59 | ``` -------------------------------------------------------------------------------- /packages/himig-components/button/ms-button.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * Copyright (c) 2025 MatteuSan 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | import { html, LitElement } from "lit"; 8 | import { customElement, property } from "lit/decorators.js"; 9 | import { ifDefined } from 'lit/directives/if-defined.js'; 10 | import { styles } from './ms-button.styles'; 11 | 12 | @customElement('ms-button') 13 | export default class MSButton extends LitElement { 14 | @property({ type: String }) type?: string; 15 | @property({ type: String }) nativeType?: string; 16 | @property({ type: String }) link?: string; 17 | @property({ type: String }) a11tyLabel?: string; 18 | @property({ type: Boolean }) isDisabled?: boolean = false; 19 | @property({ type: Function }) onClick?: () => void; 20 | 21 | static override styles = styles; 22 | 23 | private handleTypes(types: string): string { 24 | const finalTypes: string[] = []; 25 | types.split(' ').forEach((type: string) => finalTypes.push(`is-${type}`)); 26 | return finalTypes.join(' '); 27 | } 28 | 29 | private handleLinkTarget(link: string): '_self'|'_blank' { 30 | const url: URL = new URL(link); 31 | if (url.hostname !== window.location.hostname) return '_blank'; 32 | return '_self'; 33 | } 34 | 35 | private renderBase() { 36 | return html` 37 | 38 | 39 | 40 | 41 | 42 | 43 | `; 44 | } 45 | 46 | override render() { 47 | if (!this.link) { 48 | return html` 49 | 57 | `; 58 | } 59 | 60 | return html` 61 | 67 | ${this.renderBase()} 68 | 69 | `; 70 | } 71 | } -------------------------------------------------------------------------------- /packages/himig/ms-tick-field/_tick-field-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @mixin init() { 8 | .ms-tick-field { 9 | width: 100%; 10 | display: flex; 11 | flex-flow: row nowrap; 12 | align-items: center; 13 | gap: pintig.key-create('tick-field-gap', 0.5rem); 14 | transition-property: background, color, border; 15 | transition-duration: pintig.token-get('time-short'); 16 | transition-timing-function: pintig.token-get('easing'); 17 | 18 | @content; 19 | 20 | .ms-tick-field__input { 21 | display: grid; 22 | place-content: center; 23 | @include pintig.struct-radius('tick-field-input', inherit); 24 | @include pintig.color-apply('tick-field-input', pintig.$color-inherit-template); 25 | transition-property: background, color, border; 26 | transition-duration: pintig.token-get('time-short'); 27 | transition-timing-function: pintig.token-get('easing'); 28 | appearance: none; 29 | outline: none; 30 | cursor: pintig.key-create('tick-field-input-cursor', not-allowed); 31 | 32 | &:not(:disabled) { 33 | @include pintig.key-bind('tick-field-input-cursor', pointer); 34 | } 35 | 36 | &::before { 37 | content: ''; 38 | width: 0.65rem; 39 | height: 0.65rem; 40 | transform: scale(0); 41 | transition: 120ms transform ease-in-out; 42 | box-shadow: inset 1em 1em pintig.key-get('tick-field-input-fill'); 43 | color: pintig.key-get('tick-field-input-ink'); 44 | } 45 | 46 | &:checked { 47 | &::before { 48 | transform: scale(1); 49 | } 50 | } 51 | } 52 | 53 | .ms-tick-field__text { 54 | display: flex; 55 | flex-flow: column; 56 | align-items: center; 57 | @include pintig.struct-gap('tick-field-text', inherit); 58 | 59 | .ms-tick-field__label { 60 | user-select: none; 61 | @include pintig.typography-apply('tick-field', pintig.$type-inherit-template); 62 | } 63 | 64 | .ms-tick-field__helper { 65 | @include pintig.typography-apply('tick-field-helper', pintig.$type-inherit-template); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /packages/himig/ms-utils/_color-utils.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:map'; 7 | @use 'sass:list'; 8 | @use 'consts'; 9 | @use 'util-helpers' as helpers; 10 | 11 | $_breakpoints: consts.$breakpoints; 12 | $_colors: ( 13 | 'primary', 14 | 'accent', 15 | 'surface', 16 | 'error', 17 | ); 18 | $_tones: (200, 300, 400, 600, 800, 'ink'); 19 | 20 | @mixin color($include: (), $exclude: ()) { 21 | $utils-to-render: helpers.handle-include-exclude(('fill', 'ink', 'border'), $include, $exclude); 22 | 23 | @layer utils { 24 | // Background color 25 | @if list.index($utils-to-render, 'fill') { 26 | @each $color in $_colors { 27 | @each $tone in $_tones { 28 | .fill-#{$color}-#{$tone} { 29 | background-color: pintig.token-get('#{$color}-#{$tone}'); 30 | } 31 | 32 | @each $breakpoint in $_breakpoints { 33 | .\@#{$breakpoint}\:fill-#{$color}-#{$tone} { 34 | @include pintig.breakpoint-create($breakpoint) { 35 | background-color: pintig.token-get('#{$color}-#{$tone}') !important; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | // Text color 44 | @if list.index($utils-to-render, 'ink') { 45 | @each $color in $_colors { 46 | @each $tone in $_tones { 47 | .ink-#{$color}-#{$tone} { 48 | color: pintig.token-get('#{$color}-#{$tone}'); 49 | } 50 | 51 | @each $breakpoint in $_breakpoints { 52 | .\@#{$breakpoint}\:ink-#{$color}-#{$tone} { 53 | @include pintig.breakpoint-create($breakpoint) { 54 | color: pintig.token-get('#{$color}-#{$tone}') !important; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | // Border color 63 | @if list.index($utils-to-render, 'border') { 64 | @each $color in $_colors { 65 | @each $tone in $_tones { 66 | .border-#{$color}-#{$tone} { 67 | border-color: pintig.token-get('#{$color}-#{$tone}'); 68 | } 69 | 70 | @each $breakpoint in $_breakpoints { 71 | .\@#{$breakpoint}\:border-#{$color}-#{$tone} { 72 | @include pintig.breakpoint-create($breakpoint) { 73 | border-color: pintig.token-get('#{$color}-#{$tone}') !important; 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Himig 2 | 3 | ## Workflow Digest 4 | 1. Create a fork of this repository and clone it locally. 5 | 2. Do `npm install` or `yarn install` to install all dependencies. 6 | - If you're working on the documentation, be sure to `cd` into the `/docs` directory. 7 | - Or, if you're working on the tests, be sure to `cd` into the `/tests` directory. 8 | - Additionally, there are details you need to know about installing dependencies [here](#installing-dependencies). 9 | 10 | 3. Open a new branch from main with the following format: 11 | 12 | ```text 13 | [username]-[issue-number]_[issue-title] 14 | ``` 15 | 16 | 4. Code code code. 17 | 5. Create a pull request. 18 | 6. Wait for the pull request to be reviewed. 19 | - If the pull request is approved, the pull request will be merged into the main branch. 20 | 7. 🎉 Congratulations! You've successfully contributed to himig 21 | 22 | ## Details on Contributing 23 | ### Contributing Scopes 24 | When contributing to Himig, it is important to keep in mind what a contributor can code and cannot code. 25 | 26 | - Authoring new components are almost always left up to the internal team working on the design system. 27 | - Code deviating from the specifications is NOT allowed. Follow the specifications in the design docs or in the `/specs` directory. 28 | 29 | ### Installing Dependencies 30 | The current dependency list will always be compatible with the latest version of himig However, the root directory, the tests and dev versions of the docs require the current in-development version of Himig (aka the version you're working on). Which is why we're going to be using the `npm link` feature to easily link the dev version of Himig to the tests and docs. 31 | 32 | First `cd` into the source folder. 33 | ```sh 34 | $ cd src 35 | ``` 36 | 37 | And execute the `link` command. 38 | ```sh 39 | $ npm link 40 | 41 | # or 42 | 43 | $ yarn link 44 | ``` 45 | 46 | `cd` back into the root directory (or the tests, or docs directory). 47 | ```sh 48 | $ cd .. 49 | $ cd ../docs 50 | $ cd ../tests 51 | ``` 52 | 53 | And finally, execute the linking command. 54 | > NOTE: the `current-dev-version` is usually spat out in the terminal after running the first `npm link` command in the source directory. When using yarn, you usually don't have to worry about this. 55 | 56 | ```sh 57 | $ npm link @matteusan/himig@[current-dev-version] 58 | 59 | # or 60 | 61 | $ yarn link @matteusan/himig 62 | ``` 63 | 64 | ### Code Style 65 | If you're worried about code style, we've provided an `.editorconfig` file for you to consume. 66 | -------------------------------------------------------------------------------- /packages/himig/ms-card/_card-styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:list'; 7 | @use 'card-base' as base; 8 | @use 'card-theme' as theme; 9 | 10 | @mixin render( 11 | $color: (), 12 | $struct: (), 13 | $typography: (), 14 | $settings: () 15 | ) { 16 | $color: pintig.theme-merge(theme.$init-color, $color); 17 | $struct: pintig.theme-merge(theme.$init-struct, $struct); 18 | $typography: pintig.theme-merge(theme.$init-typography, $typography); 19 | $settings: pintig.theme-merge(theme.$init-settings, $settings); 20 | 21 | @layer components { 22 | @include base.init( 23 | $struct, 24 | $typography 25 | ) { 26 | @include pintig.color-border('card', pintig.theme-get($color, 'border'), 'bind'); 27 | @include pintig.struct-apply('card', ( 28 | width: ( 29 | default: pintig.theme-get($struct, 'width'), 30 | min: pintig.theme-get($struct, 'width', 'min'), 31 | max: pintig.theme-get($struct, 'width', 'max'), 32 | ), 33 | radius: pintig.theme-get($struct, 'radius'), 34 | shadow: 'none', 35 | )); 36 | 37 | @include pintig.breakpoint-create('large') { 38 | &:has(.ms-card__media) { 39 | .ms-card__details { 40 | @include pintig.color-bind('card', ( 41 | fill: linear-gradient(180deg, 42 | rgb(from pintig.token-switch(pintig.theme-get($color, 'fill')) r g b / 0%) 0%, 43 | rgb(from pintig.token-switch(pintig.theme-get($color, 'fill')) r g b / 50%) 15%, 44 | rgb(from pintig.token-switch(pintig.theme-get($color, 'fill')) r g b / 70%) 30%, 45 | rgb(from pintig.token-switch(pintig.theme-get($color, 'fill')) r g b / 90%) 80%, 46 | rgb(from pintig.token-switch(pintig.theme-get($color, 'fill')) r g b / 100%) 100% 47 | ), 48 | ink: pintig.theme-get($color, 'ink'), 49 | )); 50 | } 51 | } 52 | } 53 | 54 | &:where(.is-raised) { 55 | @include pintig.struct-bind('card', ( 56 | shadow: 'low' 57 | )); 58 | } 59 | 60 | .ms-card__details { 61 | @include pintig.color-apply('card', ( 62 | fill: pintig.theme-get($color, 'fill') 63 | )); 64 | 65 | .ms-card__subtitle { 66 | @include pintig.color-apply('card-subtitle', ( 67 | ink: rgb(from pintig.token-switch(pintig.theme-get($color, 'ink', 'subtitle')) r g b / 60%), 68 | )); 69 | } 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /packages/pintig/struct/_struct-setters.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | @use '../tools'; 7 | @use 'sass:map'; 8 | @use 'sass:list'; 9 | @use 'struct-utils' as util; 10 | @use 'struct-validators' as validator; 11 | 12 | /// Base mixin for apply() and bind() 13 | /// @access private 14 | @mixin _base($component, $theme, $settings: ()) { 15 | $_init-settings: ('intent': create); 16 | $_settings: map.merge($_init-settings, $settings); 17 | 18 | /// @type string 19 | $intent: map.get($_settings, 'intent'); 20 | 21 | $width: map.get($theme, 'width'); 22 | $height: map.get($theme, 'height'); 23 | $radius: theme.theme-get($theme, 'radius'); 24 | $padding: theme.theme-get($theme, 'padding'); 25 | $margin: theme.theme-get($theme, 'margin'); 26 | $gap: theme.theme-get($theme, 'gap'); 27 | $shadow: theme.theme-get($theme, 'shadow'); 28 | $border-style: theme.theme-get($theme, 'border-style'); 29 | $border-width: theme.theme-get($theme, 'border-width'); 30 | 31 | @if tools.is-type($theme, 'map', $custom-error: '$theme is expecting a map. Map was not given.') { 32 | @include validator.validate-struct-props(map.keys($theme)) { 33 | @if $width { 34 | @include util.width($component, $width, $intent); 35 | } 36 | @if $height { 37 | @include util.height($component, $height, $intent); 38 | } 39 | @if $radius { 40 | @include util.radius($component, $radius, $intent); 41 | } 42 | @if $padding { 43 | @include util.padding($component, $padding, $intent); 44 | } 45 | @if $margin { 46 | @include util.margin($component, $margin, $intent); 47 | } 48 | @if $gap { 49 | @include util.gap($component, $gap, $intent); 50 | } 51 | @if $border-width { 52 | @include util.border-width($component, $border-width, $intent); 53 | } 54 | @if $border-style { 55 | @include util.border-style($component, $border-style, $intent); 56 | } 57 | @if $shadow { 58 | @include util.shadow($component, $shadow, $intent); 59 | } 60 | } 61 | } 62 | } 63 | 64 | /// Mixin that applies struct styles to a component. 65 | /// @param {string} $component 66 | /// @param {map} $theme 67 | /// @return {void} void. 68 | @mixin apply($component, $theme) { 69 | @include _base($component, $theme); 70 | } 71 | 72 | /// Mixin that binds struct styles to a component with existing keys. 73 | /// @param {string} $component 74 | /// @param {map} $theme 75 | /// @return {void} void. 76 | @mixin bind($component, $theme) { 77 | @include _base($component, $theme, ( 78 | 'intent': bind 79 | )); 80 | } -------------------------------------------------------------------------------- /tests/sass/pintig/color-setters.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | :root { 9 | @include pintig.token-config( 10 | $accent: ( 11 | 400: 'ms-orange.400', 12 | ink: 'ms-neutral.200' 13 | ) 14 | ); 15 | } 16 | 17 | @include true.describe('Color Setters [module]') { 18 | @include true.it('should apply colors to a component') { 19 | @include true.assert { 20 | @include true.output { 21 | @include pintig.color-apply('component', ( 22 | fill: 'ms-orange.400', 23 | ink: 'ms-neutral.200', 24 | border: 'ms-orange.400' 25 | )); 26 | } 27 | @include true.expect { 28 | background: var(--ms-component-fill, oklch(65% 0.2 47.5deg)); 29 | color: var(--ms-component-ink, oklch(95% 0 0deg)); 30 | border-color: var(--ms-component-border, oklch(65% 0.2 47.5deg)); 31 | } 32 | } 33 | } 34 | @include true.it('shoul apply colors to a component from the theme') { 35 | @include true.assert { 36 | @include true.output { 37 | @include pintig.color-apply('component', ( 38 | fill: 'accent-400', 39 | ink: 'accent-ink', 40 | border: 'accent-400', 41 | )); 42 | } 43 | @include true.expect { 44 | background: var(--ms-component-fill, var(--ms-accent-400)); 45 | color: var(--ms-component-ink, var(--ms-accent-ink)); 46 | border-color: var(--ms-component-border, var(--ms-accent-400)); 47 | } 48 | } 49 | } 50 | 51 | @include true.it('should bind colors to a component') { 52 | @include true.assert { 53 | @include true.output { 54 | @include pintig.color-bind('component', ( 55 | fill: 'ms-orange.400', 56 | ink: 'ms-neutral.200', 57 | border: 'ms-orange.400' 58 | )); 59 | } 60 | @include true.expect { 61 | --ms-component-fill: oklch(65% 0.2 47.5deg); 62 | --ms-component-ink: oklch(95% 0 0deg); 63 | --ms-component-border: oklch(65% 0.2 47.5deg); 64 | } 65 | } 66 | } 67 | @include true.it('should bind colors to a component from the theme') { 68 | @include true.assert { 69 | @include true.output { 70 | @include pintig.color-bind('component', ( 71 | fill: 'accent-400', 72 | ink: 'accent-ink', 73 | border: 'accent-400', 74 | )); 75 | } 76 | @include true.expect { 77 | --ms-component-fill: var(--ms-accent-400); 78 | --ms-component-ink: var(--ms-accent-ink); 79 | --ms-component-border: var(--ms-accent-400); 80 | } 81 | } 82 | } 83 | } 84 | 85 | @include true.report(); -------------------------------------------------------------------------------- /tests/sass/pintig/color-utils.test.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'pkg:sass-true' as true; 7 | 8 | @include true.describe('Color Utils [module]') { 9 | @include true.it('should return a fill color') { 10 | @include true.assert { 11 | @include true.output { 12 | @include pintig.color-fill( 13 | 'component', 14 | pintig.primitive-token-get('ms-orange.400') 15 | ); 16 | } 17 | @include true.expect { 18 | background: var(--ms-component-fill, oklch(65% 0.2 47.5deg)); 19 | } 20 | } 21 | } 22 | 23 | @include true.it('should return an ink color') { 24 | @include true.assert { 25 | @include true.output { 26 | @include pintig.color-ink( 27 | 'component', 28 | pintig.primitive-token-get('ms-orange.400') 29 | ); 30 | } 31 | @include true.expect { 32 | color: var(--ms-component-ink, oklch(65% 0.2 47.5deg)); 33 | } 34 | } 35 | } 36 | 37 | @include true.it('should return a border color') { 38 | @include true.assert { 39 | @include true.output { 40 | @include pintig.color-border( 41 | 'component', 42 | pintig.primitive-token-get('ms-orange.400') 43 | ); 44 | } 45 | @include true.expect { 46 | border-color: var(--ms-component-border, oklch(65% 0.2 47.5deg)); 47 | } 48 | } 49 | } 50 | 51 | @include true.it('should return a fill color binding') { 52 | @include true.assert { 53 | @include true.output { 54 | @include pintig.color-fill( 55 | 'component', 56 | pintig.primitive-token-get('ms-orange.400'), 57 | 'bind' 58 | ); 59 | } 60 | @include true.expect { 61 | --ms-component-fill: oklch(65% 0.2 47.5deg); 62 | } 63 | } 64 | } 65 | 66 | @include true.it('should return an ink color binding') { 67 | @include true.assert { 68 | @include true.output { 69 | @include pintig.color-ink( 70 | 'component', 71 | pintig.primitive-token-get('ms-orange.400'), 72 | 'bind' 73 | ); 74 | } 75 | @include true.expect { 76 | --ms-component-ink: oklch(65% 0.2 47.5deg); 77 | } 78 | } 79 | } 80 | 81 | @include true.it('should return a border color binding') { 82 | @include true.assert { 83 | @include true.output { 84 | @include pintig.color-border( 85 | 'component', 86 | pintig.primitive-token-get('ms-orange.400'), 87 | 'bind' 88 | ); 89 | } 90 | @include true.expect { 91 | --ms-component-border: oklch(65% 0.2 47.5deg); 92 | } 93 | } 94 | } 95 | } 96 | 97 | @include true.report(); -------------------------------------------------------------------------------- /specs/foundation/structure/applying-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Applying Structure 3 | slug: /foundation/structure/applying-structure 4 | --- 5 | # Applying Structure 6 | Himig's guidelines to applying structure to an element. 7 | 8 | ## Component Structure Anatomy 9 | The component structure anatomy uses the native DOM structuring, wherein we take advantage of these parts: 10 | 11 | 1) Dimensions 12 | 2) Padding 13 | 3) Margin 14 | 4) Border 15 | 16 | ### Dimensions 17 | Dimensions define the width and height of an element in a 2D space. This is commonly associated with the `width`, and `height` CSS properties. 18 | 19 | #### SCSS 20 | For the API information, please visit the [structure composition API page](../../api/composition/structure.md). 21 | 22 | ```scss 23 | @use 'pkg:@matteusan/sentro'; 24 | 25 | .component { 26 | @include himigstruct-width('component', 'token-or-map-or-value'); 27 | } 28 | ``` 29 | 30 | ### Padding 31 | Padding defines the space inside an element. This is commonly associated with the `padding` CSS property. 32 | 33 | #### SCSS 34 | For the API information, please visit the [structure composition API page](../../api/composition/structure.md). 35 | 36 | ```scss 37 | @use 'pkg:@matteusan/sentro'; 38 | 39 | .component { 40 | @include himigstruct-padding('component', 'token-or-value'); 41 | } 42 | ``` 43 | ### Margin 44 | Margin defines the space outside an element. This is commonly associated with the `margin` CSS property. 45 | 46 | #### SCSS 47 | For the API information, please visit the [structure composition API page](../../api/composition/structure.md). 48 | 49 | ```scss 50 | @use 'pkg:@matteusan/sentro'; 51 | 52 | .component { 53 | @include himigstruct-margin('component', 'token-or-value'); 54 | } 55 | ``` 56 | 57 | ### Border 58 | Border defines the space at the edges of an element. This is commonly associated with the `border-size` CSS property. 59 | 60 | #### SCSS 61 | For the API information, please visit the [structure composition API page](../../api/composition/structure.md). 62 | 63 | ```scss 64 | @use 'pkg:@matteusan/sentro'; 65 | 66 | .component { 67 | @include himigstruct-border('component', 'token-or-value'); 68 | } 69 | ``` 70 | 71 | ## Applying structs together 72 | Himig provides an SCSS mixin that lets you apply all structs in one go. It's the `struct-apply()` mixin. It takes the component name, and the struct map. 73 | 74 | For the API information, please visit the [structure composition API page](../../api/composition/structure.md). 75 | 76 | ```scss 77 | @use 'pkg:@matteusan/himig'; 78 | 79 | .ms-component { 80 | @include himig.struct-apply('component', ( 81 | width: ( 82 | default: 'token-or-value', 83 | max: 'token-or-value' 84 | ), 85 | height: 'token-or-value', 86 | padding: 'token-or-value', 87 | margin: 'token-or-value', 88 | border: 'token-or-value' 89 | )); 90 | } 91 | ``` -------------------------------------------------------------------------------- /packages/pintig/tokens/_token-methods.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../tools'; 6 | @use 'sass:map'; 7 | @use 'sass:list'; 8 | @use 'sass:string'; 9 | @use 'token-data' as data; 10 | 11 | $_primitive-token-registry: (); 12 | 13 | @mixin register($map: ()) { 14 | $token-keys: map.keys($map); 15 | @each $token-key in $token-keys { 16 | $variant-keys: map.keys(map.get($map, $token-key)); 17 | @each $variant-key in $variant-keys { 18 | $_primitive-token-registry: list.append($_primitive-token-registry, '#{$token-key}.#{$variant-key}') !global; 19 | } 20 | } 21 | } 22 | 23 | @include register(data.$ms-tokens); 24 | 25 | @function registry-get($targets: 'all') { 26 | @if $targets == 'all' { 27 | @return $_primitive-token-registry; 28 | } 29 | 30 | @if not tools.is-empty($targets) { 31 | $targeted-tokens: (); 32 | @each $target in $targets { 33 | @each $key in $_primitive-token-registry { 34 | @if string.index($key, $target) == 1 { 35 | $targeted-tokens: list.append($targeted-tokens, $key); 36 | } 37 | } 38 | } 39 | @return $targeted-tokens; 40 | } 41 | 42 | @return $_primitive-token-registry; 43 | } 44 | 45 | /// Retrieves a token from the primitive token registry based on a given query. 46 | /// @param {PrimitiveToken} $query 47 | /// @return {*} primitive token value. 48 | @function get($query) { 49 | @if _validate($query) { 50 | $keys: tools.str-split($query, '.'); 51 | $token: map.get(data.$ms-tokens, list.nth($keys, 1)); 52 | $value: map.get($token, list.nth($keys, 2)); 53 | @return $value; 54 | } 55 | } 56 | 57 | /// Retrieves a token from the primitive token registry based on a given query. 58 | /// @param {PrimitiveToken} $query 59 | /// @param {*} $fallback 60 | /// @return {*} primitive token value. 61 | @function switch($query, $fallback: ()) { 62 | @if check($query) == true { 63 | $keys: tools.str-split($query, '.'); 64 | $token: map.get(data.$ms-tokens, list.nth($keys, 1)); 65 | $value: map.get($token, list.nth($keys, 2)); 66 | @return $value; 67 | } 68 | @if not tools.is-empty($fallback) { 69 | @return $fallback; 70 | } 71 | @return $query; 72 | } 73 | 74 | /// Soft validates if the query is a valid primitive token. 75 | /// @param {string} $query 76 | /// @return {boolean} true if valid, false if not. 77 | @function check($query) { 78 | @return _validate($query, $errors: false); 79 | } 80 | 81 | /// Validates if the query is a valid primitive token. 82 | /// @access private 83 | /// @param {string} $query 84 | /// @return {boolean|error} true if valid, false or an error if not. 85 | @function _validate($query, $errors: true) { 86 | @if not list.index($_primitive-token-registry, $query) { 87 | @if $errors { 88 | @error 'Invalid primitive token: #{$query}. Expecting a valid primitive token.'; 89 | } 90 | @return false; 91 | } 92 | 93 | @return true; 94 | } -------------------------------------------------------------------------------- /specs/foundation/components.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Components 3 | slug: /foundation/components 4 | --- 5 | # Components 6 | Components are primitive elements provided in the design system. These are carefully chosen, planned, and engineered to be reusable for any use case. 7 | 8 | The Himig components are components that got used often in projects, and are potentially re-engineered from the ground up over and over again when we initialize a new project. 9 | 10 | ## Primitive-only Approach 11 | Himig has adopted a primitive only approach to its components list. 12 | 13 | As projects grow over time, it introduces design problems specific for that project only. For example, this project requires a specific layout, or a specific style of navigation that may differ so far from the spec of the design system but still maintain core principles of what the design system stands for. 14 | 15 | The primitive only approach to components allows for flexibility in creating 'internal design systems' for each project. Each component is designed to be plugged into a design system depending on when you need it and play accordingly. Think of it as the core components of a larger design system for a product or a series of products. 16 | 17 | So now, those layout specifications and that navigation experience that's specific for that project can now be a reality, while still maintaining the integrity of the design system. 18 | 19 | ## Structure 20 | Himig components uses the Modified BEM class naming architecture. Himig still retains the block and element naming scheme. However, we replaced the verbose modifier classes (which would look like this: `ms-button--variant`) with shorter and scoped ones like this: (`is-variant`). 21 | 22 | For example, a button with a large variant would look like this: 23 | ```html 24 | 27 | ``` 28 | You might ask, does it have specificity issues? The answer is a proud no, it does not have specificity issues. And here's why: 29 | 30 | We're using the block's class name as a specifier for the variant like so: 31 | ```scss 32 | .ms-button { 33 | // CSS... 34 | 35 | &.is-large { 36 | // CSS... 37 | } 38 | } 39 | ``` 40 | This reduces the possibility of leaking the large styles to other components with a similarly named variant. The variants are scoped into the block itself. 41 | 42 | Not only this, we've also taken measure to use custom properties as the only way to modify core styles like [colors](color/index.md), [structs](structure/index.md), and [typography](typography/index.md). 43 | ```scss 44 | .ms-button { 45 | // CSS... 46 | @include himig.color-apply('button', (...)); 47 | 48 | &.is-large { 49 | // CSS... 50 | @include himig.color-bind('button', (...)); 51 | } 52 | } 53 | ``` 54 | Now if you wish to create your own variants of a primitive component, this [guide](../components/index.md#extending-components) features everything you need in creating component variants. -------------------------------------------------------------------------------- /specs/components/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Components 3 | slug: /components 4 | --- 5 | # Components 6 | Himig's components serve as primitive building blocks to build complex interfaces. These are carefully crafted to produce 7 | the same experiences all throughout the GrowStocks ecosystem. 8 | 9 | ## Instantiation 10 | All components follow the same instantiation API: Always instantiating the component inside the design system 11 | initializer (`himig.init()`) using the component's provided `render()` mixin. 12 | 13 | ```scss 14 | @use 'pkg:@matteusan/himig'; 15 | @use 'pkg:@matteusan/himig/ms-component'; 16 | 17 | @include himig.init() { 18 | @include ms-component.render(); 19 | } 20 | ``` 21 | 22 | It's as easy as that! 23 | 24 | ## Modification (HTML) 25 | Modifying a component in the markup is your first way of customizing a component according to your user interface needs. By using the component's variant classes ([or your own](#extending-components)). 26 | ```html 27 | 28 | 31 | 32 | 33 | 36 | ``` 37 | 38 | ## Modification (SCSS) 39 | Modifying a component in the SCSS is your second way of customizing a component. By using the parameters in the component initializer, you can globally modify the component's properties. 40 | ```scss 41 | @use 'pkg:@matteusan/himig'; 42 | @use 'pkg:@matteusan/himig/ms-component'; 43 | 44 | @include himig.init() { 45 | @include ms-component.render( 46 | $color: ( 47 | fill: ( 48 | default: 'ms-green.400', 49 | hover: 'ms-green.300', 50 | active: 'ms-green.200', 51 | focus: 'ms-green.300', 52 | disabled: 'ms-green.800', 53 | ), 54 | ink: 'ms-white.200', 55 | border: ( 56 | default: 'ms-green.400', 57 | hover: 'ms-green.300', 58 | active: 'ms-green.200', 59 | focus: 'ms-green.300', 60 | disabled: 'ms-green.800', 61 | ), 62 | ) 63 | ); 64 | } 65 | ``` 66 | 67 | ## Extending Components 68 | Creating your own variant component uses Himig's provided extension API. Instantiate a new variant by calling the `extend()` mixin and filling in the variant name parameter. 69 | 70 | ```scss 71 | @use 'pkg:@matteusan/himig'; 72 | @use 'pkg:@matteusan/himig/ms-component'; 73 | 74 | @include himig.init() { 75 | @include ms-component.extend('large') { 76 | // 77 | } 78 | } 79 | ``` 80 | 81 | Make adjustments to the variant by calling the modifier mixins. These mixins are: `color()`, `struct()`, and `typography`. 82 | 83 | ```scss 84 | @use 'pkg:@matteusan/himig'; 85 | @use 'pkg:@matteusan/himig/ms-component'; 86 | 87 | @include himig.init() { 88 | @include ms-component.extend('large') { 89 | @include ms-component.struct(( 90 | padding: ('md' 'xl') 91 | )); 92 | @include ms-component.typography(( 93 | size: 'md', 94 | )); 95 | } 96 | } 97 | ``` -------------------------------------------------------------------------------- /packages/pintig/typography/_typography-utils.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | @use '../tokens' as token; 7 | 8 | /// Mixin for applying font family. 9 | /// @param {string} $component 10 | /// @param {string} $family 11 | /// @param {string} $intent 12 | /// @return {void} font family styles. 13 | @mixin family($component, $family, $intent: 'create') { 14 | @include theme.property( 15 | font-family, 16 | ('#{$component}-family', theme.token-switch('family-#{$family}', token.primitive-token-switch('family.#{$family}', $family))), 17 | $intent 18 | ); 19 | } 20 | 21 | /// Mixin for applying font size. 22 | /// @param {string} $component 23 | /// @param {string} $size 24 | /// @param {string} $intent 25 | /// @return {void} font size styles. 26 | @mixin size($component, $size, $intent: 'create') { 27 | @include theme.property( 28 | font-size, 29 | ('#{$component}-size', theme.token-switch('size-#{$size}', token.primitive-token-switch('size.#{$size}', $size))), 30 | $intent 31 | ); 32 | } 33 | 34 | /// Mixin for applying font weight. 35 | /// @param {string} $component 36 | /// @param {string} $weight 37 | /// @param {string} $intent 38 | /// @return {void} font weight styles. 39 | @mixin weight($component, $weight, $intent: 'create') { 40 | @include theme.property( 41 | font-weight, 42 | ('#{$component}-weight', theme.token-switch('weight-#{$weight}', token.primitive-token-switch('weight.#{$weight}', $weight))), 43 | $intent 44 | ); 45 | } 46 | 47 | /// Mixin for applying line height. 48 | /// @param {string} $component 49 | /// @param {string} $line-height 50 | /// @param {string} $intent 51 | /// @return {void} line height styles. 52 | @mixin line-height($component, $line-height, $intent: 'create') { 53 | @include theme.property( 54 | line-height, 55 | ('#{$component}-line-height', theme.token-switch('line-height-#{$line-height}', token.primitive-token-switch('line-height.#{$line-height}', $line-height))), 56 | $intent 57 | ); 58 | } 59 | 60 | /// Mixin for applying letter spacing. 61 | /// @param {string} $component 62 | /// @param {string} $squeeze 63 | /// @param {string} $intent 64 | /// @return {void} letter spacing styles. 65 | @mixin squeeze($component, $squeeze, $intent: 'create') { 66 | @include theme.property( 67 | letter-spacing, 68 | ('#{$component}-squeeze', theme.token-switch('squeeze-#{$squeeze}', token.primitive-token-switch('squeeze.#{$squeeze}', $squeeze))), 69 | $intent 70 | ); 71 | } 72 | 73 | /// Mixin for applying font stretch. 74 | /// @param {string} $component 75 | /// @param {string} $stretch 76 | /// @param {string} $intent 77 | /// @return {void} letter spacing styles. 78 | @mixin stretch($component, $stretch, $intent: 'create') { 79 | @include theme.property( 80 | font-stretch, 81 | ('#{$component}-stretch', theme.token-switch('stretch-#{$stretch}', token.primitive-token-switch('stretch.#{$stretch}', $stretch))), 82 | $intent 83 | ); 84 | } -------------------------------------------------------------------------------- /packages/himig/ms-select-field/_select-field-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @use 'sass:math'; 8 | 9 | @mixin init($top-label-distance, $left-label-distance, $top-padding) { 10 | .ms-select-field { 11 | width: 100%; 12 | display: flex; 13 | flex-flow: column nowrap; 14 | position: relative; 15 | 16 | @content; 17 | 18 | &:has(.ms-select-field__label) { 19 | .ms-select-field__input { 20 | padding-block-start: calc(#{$top-padding} + 1rem) !important; 21 | } 22 | } 23 | 24 | &:has(.ms-select-field__input:focus), 25 | &:has(.ms-select-field__input:valid), 26 | &:has(.ms-select-field__input > option:checked),{ 27 | .ms-select-field__label { 28 | top: math.div($top-label-distance, 2) !important; 29 | @include pintig.typography-bind('select-field-label', ( 30 | family: 'small', 31 | size: 'small', 32 | )); 33 | } 34 | } 35 | 36 | .ms-select-field__label { 37 | position: absolute; 38 | top: calc(#{$top-label-distance}); 39 | left: calc(#{$left-label-distance} + 2px); 40 | @include pintig.color-apply('select-field-label', ( 41 | fill: rgba(0 0 0 / 0), 42 | ink: inherit, 43 | )); 44 | @include pintig.struct-apply('select-field-label', ( 45 | padding: ('xs' 'xs' 0 0), 46 | )); 47 | @include pintig.typography-apply('select-field-label', pintig.$type-inherit-template); 48 | } 49 | 50 | .ms-select-field__input { 51 | @include pintig.struct-apply('select-field', ( 52 | width: 100%, 53 | margin: inherit, 54 | radius: inherit, 55 | border-width: 2px, 56 | border-style: solid, 57 | shadow: 'none', 58 | )); 59 | padding: pintig.key-get('select-field-padding'); 60 | @include pintig.color-apply('select-field', ( 61 | fill: inherit, 62 | ink: inherit, 63 | border: inherit 64 | )); 65 | @include pintig.typography-apply('select-field', ( 66 | family: inherit, 67 | size: inherit, 68 | weight: inherit, 69 | line-height: inherit, 70 | )); 71 | outline: none; 72 | resize: none; 73 | cursor: text; 74 | -webkit-appearance: caret; 75 | transition-property: background, color, border; 76 | transition-duration: pintig.token-get('time-short'); 77 | transition-timing-function: pintig.token-get('easing'); 78 | } 79 | 80 | .ms-select-field__helper { 81 | @include pintig.color-apply('select-field-helper', ( 82 | ink: inherit, 83 | )); 84 | @include pintig.typography-apply('select-field-helper', ( 85 | family: inherit, 86 | size: inherit, 87 | weight: inherit, 88 | line-height: inherit, 89 | )); 90 | transition-property: background, color, border; 91 | transition-duration: pintig.token-get('time-short'); 92 | transition-timing-function: pintig.token-get('easing'); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /packages/himig-components/button/ms-button.styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig' with ($module-mode: true); 6 | @use 'sass:list'; 7 | @use 'sass:selector'; 8 | 9 | @use '../helpers'; 10 | @use 'button-base'; 11 | @use 'button-theme' as theme; 12 | @use 'button-mixins' as style; 13 | 14 | $_states: ('error'); 15 | 16 | $_states: ('error'); 17 | 18 | @include button-base.init() { 19 | outline-offset: 2px; 20 | 21 | $color: theme.$init-color; 22 | $struct: theme.$init-struct; 23 | $typography: theme.$init-typography; 24 | $settings: theme.$init-settings; 25 | 26 | $fills: ( 27 | default: pintig.theme-get($color, 'fill'), 28 | hover: pintig.theme-get($color, 'fill', 'hover'), 29 | focus: pintig.theme-get($color, 'fill', 'focus'), 30 | active: pintig.theme-get($color, 'fill', 'active'), 31 | disabled: pintig.theme-get($color, 'fill', 'disabled'), 32 | ); 33 | 34 | $inks: ( 35 | default: pintig.theme-get($color, 'ink'), 36 | hover: pintig.theme-get($color, 'ink', 'hover'), 37 | focus: pintig.theme-get($color, 'ink', 'focus'), 38 | active: pintig.theme-get($color, 'ink', 'active'), 39 | disabled: pintig.theme-get($color, 'ink', 'disabled'), 40 | ); 41 | 42 | $borders: ( 43 | default: pintig.theme-get($color, 'border'), 44 | hover: pintig.theme-get($color, 'border', 'hover'), 45 | focus: pintig.theme-get($color, 'border', 'focus'), 46 | active: pintig.theme-get($color, 'border', 'active'), 47 | disabled: pintig.theme-get($color, 'border', 'disabled'), 48 | ); 49 | 50 | @include pintig.struct-apply('button', $struct); 51 | @include pintig.typography-apply('button', $typography); 52 | 53 | @include helpers.state('[disabled]') { 54 | cursor: default; 55 | pointer-events: none; 56 | } 57 | 58 | @include style.text( 59 | $fills: $fills, 60 | $inks: $inks, 61 | $borders: $borders, 62 | ); 63 | 64 | @include style.outlined( 65 | $fills: $fills, 66 | $inks: $inks, 67 | $borders: $borders, 68 | ); 69 | 70 | @include style.filled( 71 | $fills: $fills, 72 | $inks: $inks, 73 | $borders: $borders, 74 | ); 75 | 76 | @include helpers.state(':not(:disabled)', '.is-raised') { 77 | @include pintig.struct-shadow('button', 'xs'); 78 | 79 | @include helpers.state(':hover', '.is-raised') { 80 | @include pintig.struct-shadow('button', 'xs'); 81 | } 82 | 83 | @include helpers.state(':active', '.is-raised') { 84 | @include pintig.struct-shadow('button', 'sm'); 85 | } 86 | } 87 | 88 | &.is-fullwidth { 89 | @include pintig.struct-width('button', ( 90 | default: 100%, 91 | min: auto, 92 | max: 100% 93 | ), 'bind'); 94 | } 95 | 96 | &.is-icon-only { 97 | @include pintig.struct-bind('button', ( 98 | width: (min: auto), 99 | padding: list.nth(pintig.theme-get($struct, 'padding'), 1) 100 | )); 101 | 102 | > .label { 103 | display: none; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "himig", 3 | "version": "1.8.2", 4 | "description": "MatteuSan's personal design system for building user interfaces. ", 5 | "main": "packages/himig/_index.scss", 6 | "files": [ 7 | "packages/**/*" 8 | ], 9 | "repository": "https://github.com/MatteuSan/himig", 10 | "author": "Matteu ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build:lit": "pnpm run lit:build && pnpm run lit:compile", 14 | "lit:css": "pnpm run lit:scss && tsx scripts/scss-to-lit.ts", 15 | "lit:scss": "pnpm run && sass packages/himig-components:packages/himig-components --style=compressed --pkg-importer=node --load-path=node_modules --no-source-map", 16 | "lit:ts": "tsc --pretty --project \"./config/tsconfig.lit.json\"", 17 | "lit:build": "pnpm run lit:css && pnpm run lit:ts", 18 | "lit:compile": "rollup -c", 19 | "pack:dist": "npm pack --pack-destination=\"./dist\"", 20 | "pack:root": "npm pack --pack-destination=\"./\"", 21 | "pub:core:patch": "cd packages/pintig && npm version patch --force && npm publish", 22 | "pub:main:patch": "cd packages/himig && npm version patch --force && npm publish", 23 | "pub:core:minor": "cd packages/pintig && npm version minor --force && npm publish", 24 | "pub:main:minor": "cd packages/himig && npm version minor --force && npm publish", 25 | "pub:core:major": "cd packages/pintig && npm version major --force && npm publish", 26 | "pub:main:major": "cd packages/himig && npm version major --force && npm publish", 27 | "pub:patch": "npm version patch --force && pnpm pub:core:patch && cd ../../.. && pnpm pub:main:patch", 28 | "pub:minor": "npm version minor --force && pnpm pub:core:minor && cd ../../.. && pnpm pub:main:minor", 29 | "pub:major": "npm version major --force && pnpm pub:core:major && cd ../../.. && pnpm pub:main:major", 30 | "run:docs": "cd documentation && yarn dev", 31 | "test:html": "pnpm i && sass tests/sass/with-html.test.scss:tests/sass/with-html.test.css --pkg-importer=node --load-path=node_modules --no-source-map --style=compressed", 32 | "test:sass": "pnpm i && jest --config=\"jest.config.ts\"" 33 | }, 34 | "devDependencies": { 35 | "@matteusan/himig": "file:./packages/himig", 36 | "@matteusan/himig-components": "file:./packages/himig-components", 37 | "@matteusan/pintig": "file:./packages/pintig", 38 | "@matteusan/sentro": "^1.4.0", 39 | "@rollup/plugin-babel": "^6.0.4", 40 | "@rollup/plugin-multi-entry": "^6.0.1", 41 | "@rollup/plugin-node-resolve": "^15.2.3", 42 | "@rollup/plugin-replace": "^5.0.2", 43 | "@rollup/plugin-terser": "^0.4.4", 44 | "@rollup/plugin-typescript": "^11.1.6", 45 | "@types/jest": "^29.5.12", 46 | "@types/node": "^20.12.7", 47 | "@web/rollup-plugin-copy": "^0.5.1", 48 | "@web/rollup-plugin-html": "^2.3.0", 49 | "@webcomponents/webcomponentsjs": "^2.8.0", 50 | "glob": "^10.3.12", 51 | "gulp": "^4.0.2", 52 | "gulp-cli": "^3.0.0", 53 | "gulp-sass": "^5.1.0", 54 | "jest": "^29.7.0", 55 | "jest-environment-jsdom": "^29.7.0", 56 | "lit": "^3.3.0", 57 | "postcss-scss": "^4.0.9", 58 | "rollup": "^4.38.0", 59 | "rollup-plugin-minify-html-literals": "^1.2.6", 60 | "rollup-plugin-summary": "^2.0.0", 61 | "rollup-plugin-terser": "^7.0.2", 62 | "sass": "^1.86.1", 63 | "sass-true": "^8.0.0", 64 | "ts-jest": "^29.1.0", 65 | "ts-lit-plugin": "^2.0.2", 66 | "ts-node": "^10.9.1", 67 | "tsx": "^4.8.1", 68 | "typescript": "^5.4.5" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/himig/ms-form-field/_form-field-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | 7 | @use 'sass:math'; 8 | 9 | @mixin init($top-label-distance, $left-label-distance, $top-padding) { 10 | .ms-form-field { 11 | width: 100%; 12 | display: flex; 13 | flex-flow: column nowrap; 14 | position: relative; 15 | 16 | @content; 17 | 18 | .ms-form-field__label { 19 | position: absolute; 20 | top: calc(#{$top-label-distance}); 21 | left: calc(#{$left-label-distance} + 2px); 22 | @include pintig.color-apply('form-field-label', ( 23 | fill: rgba(0 0 0 / 0), 24 | ink: inherit, 25 | )); 26 | @include pintig.struct-apply('form-field-label', ( 27 | padding: ('xs' 'xs' 0 0), 28 | )); 29 | @include pintig.typography-apply('form-field-label', pintig.$type-inherit-template); 30 | transition-property: top, font-size; 31 | transition-duration: pintig.token-get('time-short'); 32 | transition-timing-function: pintig.token-get('easing'); 33 | cursor: text; 34 | } 35 | 36 | &:has(.ms-form-field__input:focus), 37 | &:has(.ms-form-field__input:not(:placeholder-shown)), { 38 | .ms-form-field__label { 39 | top: math.div($top-label-distance, 2); 40 | @include pintig.typography-bind('form-field-label', ( 41 | family: 'small', 42 | size: 'small', 43 | )); 44 | } 45 | } 46 | 47 | &:has(.ms-form-field__label) { 48 | .ms-form-field__input { 49 | @include pintig.key-bind('form-field-placeholder-ink', rgba(0 0 0 / 0) !important); 50 | padding-block-start: calc(#{$top-padding} + 1rem) !important; 51 | } 52 | } 53 | 54 | .ms-form-field__input { 55 | @include pintig.struct-apply('form-field', ( 56 | width: ( 57 | default: 100%, 58 | min: inherit, 59 | max: inherit 60 | ), 61 | margin: 0, 62 | radius: 0, 63 | border-width: 2px, 64 | border-style: solid, 65 | shadow: 'none', 66 | )); 67 | padding: pintig.key-get('form-field-padding'); 68 | @include pintig.color-apply('form-field', ( 69 | fill: inherit, 70 | ink: inherit, 71 | border: inherit 72 | )); 73 | @include pintig.typography-apply('form-field', ( 74 | family: inherit, 75 | size: inherit, 76 | weight: inherit, 77 | line-height: inherit, 78 | )); 79 | outline: none; 80 | resize: none; 81 | cursor: text; 82 | -webkit-appearance: caret; 83 | transition-property: background, color, border; 84 | transition-duration: pintig.token-get('time-short'); 85 | transition-timing-function: pintig.token-get('easing'); 86 | 87 | &::placeholder { 88 | color: pintig.key-create('form-field-placeholder-ink', inherit); 89 | user-select: none !important; 90 | } 91 | } 92 | 93 | .ms-form-field__helper { 94 | @include pintig.color-apply('form-field-helper', ( 95 | ink: inherit, 96 | )); 97 | @include pintig.typography-apply('form-field-helper', ( 98 | family: inherit, 99 | size: inherit, 100 | weight: inherit, 101 | line-height: inherit, 102 | )); 103 | transition-property: background, color, border; 104 | transition-duration: pintig.token-get('time-short'); 105 | transition-timing-function: pintig.token-get('easing'); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /packages/himig-components/form-field/_form-field-base.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:math'; 7 | 8 | @mixin init($top-label-distance, $left-label-distance, $top-padding) { 9 | .ms-form-field { 10 | width: 100%; 11 | display: flex; 12 | flex-flow: column nowrap; 13 | position: relative; 14 | 15 | @content; 16 | 17 | .ms-form-field__label { 18 | position: absolute; 19 | top: calc(#{$top-label-distance}); 20 | left: calc(#{$left-label-distance} + 2px); 21 | @include pintig.color-apply('form-field-label', ( 22 | fill: rgba(0 0 0 / 0), 23 | ink: inherit, 24 | )); 25 | @include pintig.struct-apply('form-field-label', ( 26 | padding: ('xs' 'xs' 0 0), 27 | )); 28 | @include pintig.typography-apply('form-field-label', pintig.$type-inherit-template); 29 | transition-property: background, color, border; 30 | transition-duration: pintig.token-get('time-short'); 31 | transition-timing-function: pintig.token-get('easing'); 32 | cursor: text; 33 | } 34 | 35 | &:has(.ms-form-field__input:focus), 36 | &:has(.ms-form-field__input:not(:placeholder-shown)), { 37 | .ms-form-field__label { 38 | top: math.div($top-label-distance, 2); 39 | @include pintig.typography-bind('form-field-label', ( 40 | family: 'small', 41 | size: 'small', 42 | )); 43 | } 44 | } 45 | 46 | &:has(.ms-form-field__label) { 47 | .ms-form-field__input { 48 | @include pintig.key-bind('form-field-placeholder-ink', rgba(0 0 0 / 0) !important); 49 | padding-block-start: calc(#{$top-padding} + 1rem) !important; 50 | } 51 | } 52 | 53 | .ms-form-field__input { 54 | @include pintig.struct-apply('form-field', ( 55 | width: ( 56 | default: 100%, 57 | min: inherit, 58 | max: inherit 59 | ), 60 | margin: 0, 61 | radius: 0, 62 | border-width: 2px, 63 | border-style: solid, 64 | shadow: 'none', 65 | )); 66 | padding: pintig.key-get('form-field-padding'); 67 | @include pintig.color-apply('form-field', ( 68 | fill: inherit, 69 | ink: inherit, 70 | border: inherit 71 | )); 72 | @include pintig.typography-apply('form-field', ( 73 | family: inherit, 74 | size: inherit, 75 | weight: inherit, 76 | line-height: inherit, 77 | )); 78 | outline: none; 79 | resize: none; 80 | cursor: text; 81 | -webkit-appearance: caret; 82 | transition-property: background, color, border; 83 | transition-duration: pintig.token-get('time-short'); 84 | transition-timing-function: pintig.token-get('easing'); 85 | 86 | &::placeholder { 87 | color: pintig.key-create('form-field-placeholder-ink', inherit); 88 | user-select: none !important; 89 | } 90 | } 91 | 92 | .ms-form-field__helper { 93 | @include pintig.color-apply('form-field-helper', ( 94 | ink: inherit, 95 | )); 96 | @include pintig.typography-apply('form-field-helper', ( 97 | family: inherit, 98 | size: inherit, 99 | weight: inherit, 100 | line-height: inherit, 101 | )); 102 | transition-property: background, color, border; 103 | transition-duration: pintig.token-get('time-short'); 104 | transition-timing-function: pintig.token-get('easing'); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /specs/foundation/color/applying-colors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Applying Colors 3 | slug: /foundation/color/applying-colors 4 | --- 5 | # Applying Colors 6 | Himig's guidelines to applying colors to an element. 7 | 8 | ## Component Color Anatomy 9 | When applying color to a component, we generally apply it to three main parts of an element/component: 10 | 11 | 1) The container itself 12 | 2) The content inside 13 | 3) The borders 14 | 15 | ![Color Anatomy](../_media/color-anatomy.png) 16 | 17 | ### Fill 18 | The fill color is the color applied to the container itself. This generally comprises about 70% of an element's color. 19 | The fill color is commonly associated with the `background-color` CSS property. 20 | 21 | This color's value can either be a [primitive token](../tokens.md#primitive-tokens), a [theme token](../tokens.md#theme-tokens), or raw value from the [specified valid raw values](#valid-raw-colors). 22 | 23 | #### SCSS 24 | For the API information, please visit the [color composition API page](../../api/composition/color.md). 25 | 26 | ```scss 27 | @use 'pkg:@matteusan/himig'; 28 | 29 | .ms-component { 30 | @include himig.color-fill('component', 'token-or-value'); 31 | } 32 | ``` 33 | 34 | ### Ink 35 | The ink color is the color applied to the content inside the component, usually text. The ink color is commonly 36 | associated with the `color` CSS property. 37 | 38 | This color's value can either be a [primitive token](../tokens.md#primitive-tokens), a [theme token](../tokens.md#theme-tokens), or raw value from the [specified valid raw values](#valid-raw-colors). 39 | 40 | #### SCSS 41 | For the API information, please visit the [color composition API page](../../api/composition/color.md). 42 | 43 | ```scss 44 | @use 'pkg:@matteusan/himig'; 45 | 46 | .ms-component { 47 | @include himig.color-ink('component', 'token-or-value'); 48 | } 49 | ``` 50 | 51 | ### Border 52 | The border color is the color applied to the edges of an element. The border color is commonly associated with the `border-color` CSS property. 53 | 54 | This color's value can either be a [primitive token](../tokens.md#primitive-tokens), a [theme token](../tokens.md#theme-tokens), or raw value from the [specified valid raw values](#valid-raw-colors). 55 | 56 | #### SCSS 57 | For the API information, please visit the [color composition API page](../../api/composition/color.md). 58 | 59 | ```scss 60 | @use 'pkg:@matteusan/himig'; 61 | 62 | .ms-component { 63 | @include himig.color-border('component', 'token-or-value'); 64 | } 65 | ``` 66 | 67 | ## Applying the colors together 68 | Himig provides an SCSS mixin that lets you apply all three in one go. It's the `color-apply()` mixin. It takes the component name, and the color map. 69 | 70 | For the API information, please visit the [color composition API page](../../api/composition/color.md). 71 | 72 | ```scss 73 | @use 'pkg:@matteusan/himig'; 74 | 75 | .ms-component { 76 | @include himig.color-apply('component', ( 77 | fill: 'token-or-value', 78 | ink: 'token-or-value', 79 | border: 'token-or-value' 80 | )); 81 | } 82 | ``` 83 | 84 | ### What if we don't want a color on the `x` part? 85 | The design system doesn't prohibit you to use the **transparent color** for a specific part of the component. In fact, it is used in the outlined styles of a component (see [outlined button style](https://github.com/MatteuSan/himig/blob/main/packages/himig/ms-button/_button-mixins.scss#L91-L93)). 86 | 87 | ## Valid Raw Colors 88 | Here's a list of all the valid raw colors you can use: 89 | 90 | | Color | 91 | |---------------------------------| 92 | | `inherit` | 93 | | `transparent` | 94 | | `rgba(, 0%)` | 95 | | `rgba( / 0%)` | 96 | | `rgba(, , , 0)` | 97 | | `oklch(% deg / %)` | -------------------------------------------------------------------------------- /specs/foundation/typography/applying-typography.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Applying Typography 3 | slug: /foundation/typography/applying-typography 4 | --- 5 | # Applying Typography 6 | Himig's guidelines to using typography in interfaces. 7 | 8 | ## Content Typography Anatomy 9 | A content's typography can be defined in the following properties: 10 | 11 | 1) Family 12 | 2) Size 13 | 3) Weight 14 | 4) Line Height 15 | 5) Squeeze 16 | 6) Stretch 17 | 18 | ### Family 19 | Font family defines the style of the content. This can signify text hierarchy, emphasis, or just a stylistic choice. 20 | 21 | #### SCSS 22 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 23 | 24 | ```scss 25 | @use 'pkg:@matteusan/himig'; 26 | 27 | .ms-component { 28 | @include himig.typography-family('component', 'token-or-value'); 29 | } 30 | ``` 31 | 32 | ### Size 33 | Font size defines the size of the content. This can be useful when you want to add hierarchy in your content. 34 | 35 | #### SCSS 36 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 37 | 38 | ```scss 39 | @use 'pkg:@matteusan/himig'; 40 | 41 | .ms-component { 42 | @include himig.typography-size('component', 'token-or-value'); 43 | } 44 | ``` 45 | 46 | ### Weight 47 | Font weight defines how heavy the text looks. This is particularly useful when adding hierarchy or emphasis in your 48 | content. 49 | 50 | #### SCSS 51 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 52 | 53 | ```scss 54 | @use 'pkg:@matteusan/himig'; 55 | 56 | .ms-component { 57 | @include himig.typography-weight('component', 'token-or-value'); 58 | } 59 | ``` 60 | 61 | ### Line Height 62 | Line height defines the vertical space in your content. This lets your content breathe instead of being cramped to ease 63 | the user's reading experience. 64 | 65 | #### SCSS 66 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 67 | 68 | ```scss 69 | @use 'pkg:@matteusan/himig'; 70 | 71 | .ms-component { 72 | @include himig.typography-line-height('component', 'token-or-value'); 73 | } 74 | ``` 75 | 76 | ### Squeeze (Letter Spacing) 77 | Squeeze defines the horizontal space in between the letters. This is especially useful in scaling font sizes. 78 | 79 | #### SCSS 80 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 81 | 82 | ```scss 83 | @use 'pkg:@matteusan/himig'; 84 | 85 | .ms-component { 86 | @include himig.typography-squeeze('component', 'token-or-value'); 87 | } 88 | ``` 89 | 90 | ### Stretch (Font Stretch) 91 | Stretch defines your font's visual width. This ranges from condensed to expanded. 92 | 93 | #### SCSS 94 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 95 | 96 | ```scss 97 | @use 'pkg:@matteusan/himig'; 98 | 99 | .ms-component { 100 | @include himig.typography-stretch('component', 'token-or-value'); 101 | } 102 | ``` 103 | 104 | ## Applying typography styles together 105 | Himig provides an SCSS mixin that lets you apply all properties in one go. It's the `typography-apply()` mixin. It takes the component name, and the typography styles map. 106 | 107 | For the API information, please visit the [typography composition API page](../../api/composition/typography.md). 108 | 109 | ```scss 110 | @use 'pkg:@matteusan/himig'; 111 | 112 | .ms-component { 113 | @include himig.typography-apply('component', ( 114 | family: 'token-or-value', 115 | size: 'token-or-value', 116 | weight: 'token-or-value', 117 | line-height: 'token-or-value', 118 | squeeze: 'token-or-value', 119 | stretch: 'token-or-value' 120 | )); 121 | } 122 | ``` -------------------------------------------------------------------------------- /specs/foundation/typography/typography-tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Typography Tokens 3 | slug: /foundation/typography/tokens 4 | --- 5 | # Typography Tokens 6 | Himig provides an extensive list of carefully crafted, pre-made typography tokens ready to go. 7 | 8 | ## Typography Token Schema 9 | The typography tokens follow a specific schema to make indexing much easier. Each token type follows its own schema: 10 | 11 | | Token | Schema | 12 | |---------------|-------------------------------------------------------| 13 | | `family` | Descriptive font name-based schema. | 14 | | `size` | `none`-`xs-xl` schema; extends to a `2xl-6xl` schema. | 15 | | `weight` | Descriptive `light-black` schema. | 16 | | `line-height` | Descriptive `x-short-x-tall` schema. | 17 | | `squeeze` | Descriptive `x-tight-x-loose` schema. | 18 | | `stretch` | Descriptive `x-condensed-x-wide` schema. | 19 | 20 | ### Family Tokens 21 | | Token | Value | 22 | |---------------------|-------------------------------| 23 | | `family.mono` | `'Jetbrains Mono', monospace` | 24 | | `family.sans-serif` | `'Inter', sans-serif` | 25 | | `family.serif` | `'Noto Serif Display', serif` | 26 | 27 | 28 | ### Size Tokens 29 | | Token | Value | 30 | |------------|-------------------------------------------------------------------------------------| 31 | | `size.xs` | `clamp(#{tools.to-rem(9.60px)}, 0.6718rem + 0.1409vw, #{tools.to-rem(12.00px)})` | 32 | | `size.sm` | `clamp(#{tools.to-rem(12.00px)}, 0.8048rem + 0.3509vw, #{tools.to-rem(16.00px)})` | 33 | | `size.md` | `clamp(#{tools.to-rem(15.00px)}, 0.9594rem + 0.6716vw, #{tools.to-rem(21.33px)})` | 34 | | `size.lg` | `clamp(#{tools.to-rem(18.75px)}, 1.1372rem + 1.15vw, #{tools.to-rem(28.43px)})` | 35 | | `size.xl` | `clamp(#{tools.to-rem(23.44px)}, 1.3387rem + 1.8515vw, #{tools.to-rem(37.90px)})` | 36 | | `size.2xl` | `clamp(#{tools.to-rem(29.30px)}, 1.563rem + 2.8663vw, #{tools.to-rem(50.52px)})` | 37 | | `size.3xl` | `clamp(#{tools.to-rem(36.62px)}, 1.8066rem + 4.3184vw, #{tools.to-rem(67.34px)})` | 38 | | `size.4xl` | `clamp(#{tools.to-rem(45.78px)}, 2.0621rem + 6.3786vw, #{tools.to-rem(89.76px)})` | 39 | | `size.5xl` | `clamp(#{tools.to-rem(57.22px)}, 2.3163rem + 9.2803vw, #{tools.to-rem(119.66px)})` | 40 | | `size.6xl` | `clamp(#{tools.to-rem(71.53px)}, 2.5469rem + 13.3428vw, #{tools.to-rem(159.50px)})` | 41 | 42 | 43 | ### Weight Tokens 44 | | Token | Value | 45 | |-----------------|-------| 46 | | `weight.light` | `200` | 47 | | `weight.normal` | `400` | 48 | | `weight.bold` | `700` | 49 | | `weight.black` | `900` | 50 | 51 | 52 | ### Line Height Tokens 53 | | Token | Value | 54 | |-------------------------|--------| 55 | | `line-height.x-tall` | `2.25` | 56 | | `line-height.tall` | `2` | 57 | | `line-height.broad` | `2` | 58 | | `line-height.normal` | `1.5` | 59 | | `line-height.short` | `1.25` | 60 | | `line-height.x-short` | `1.1` | 61 | | `line-height.condensed` | `1.25` | 62 | 63 | ### Squeeze Tokens 64 | | Token | Value | 65 | |-------------------|----------------------| 66 | | `squeeze.x-tight` | `tools.to-rem(-2px)` | 67 | | `squeeze.tight` | `tools.to-rem(-1px)` | 68 | | `squeeze.normal` | `normal` | 69 | | `squeeze.loose` | `tools.to-rem(1px)` | 70 | | `squeeze.x-loose` | `tools.to-rem(2px)` | 71 | 72 | ### Stretch Tokens 73 | | Token | Value | 74 | |-----------------------|-------------------| 75 | | `stretch.x-condensed` | `ultra-condensed` | 76 | | `stretch.condensed` | `condensed` | 77 | | `stretch.normal` | `normal` | 78 | | `stretch.wide` | `expanded` | 79 | | `stretch.x-wide` | `ultra-expanded` | 80 | 81 | -------------------------------------------------------------------------------- /specs/foundation/structure/structure-tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Structure Tokens 3 | slug: /foundation/structure/tokens 4 | --- 5 | # Structure Tokens 6 | Himig provides an extensive list of carefully crafted, pre-made structure tokens ready to go. 7 | 8 | ## Structure Token Schema 9 | The structure tokens follow a specific schema to make indexing much easier. The tokens follow a `none`-`xs-xl` schema 10 | and extends to a `2xl-6xl` schema when needed. 11 | 12 | The structure token schema depends on the type of structure property. For example, `border-size` tokens are different 13 | from the `spacing` and `el-spacing` tokens. 14 | 15 | ### Spacing Tokens 16 | Spacing tokens are used to space out (usually large or large sets of) elements outside and inside. The spacing tokens borrow the ** 17 | base-8** approach used by GitHub in their [Primer Design System](https://primer.style/css/support/spacing#spacing-scale) 18 | . 19 | 20 | | Token | Value | 21 | |----------------|---------| 22 | | `spacing.none` | `0` | 23 | | `spacing.xs` | `4px` | 24 | | `spacing.sm` | `8px` | 25 | | `spacing.md` | `16px` | 26 | | `spacing.lg` | `24px` | 27 | | `spacing.xl` | `32px` | 28 | | `spacing.2xl` | `48px` | 29 | | `spacing.3xl` | `64px` | 30 | | `spacing.4xl` | `80px` | 31 | | `spacing.5xl` | `96px` | 32 | | `spacing.6xl` | `112px` | 33 | 34 | ### Element Spacing Tokens 35 | Element spacing is different from the regular spacing. These tokens are specifically used for components like buttons, 36 | form fields, etc. 37 | 38 | | Token | Value | 39 | |-------------------|-----------| 40 | | `el-spacing.none` | `0` | 41 | | `el-spacing.xs` | `0.1rem` | 42 | | `el-spacing.sm` | `0.3rem` | 43 | | `el-spacing.md` | `0.7rem` | 44 | | `el-spacing.lg` | `1.2rem` | 45 | | `el-spacing.xl` | `2.3rem` | 46 | | `el-spacing.2xl` | `3.4rem` | 47 | | `el-spacing.3xl` | `4.5rem` | 48 | | `el-spacing.4xl` | `6.1rem` | 49 | | `el-spacing.5xl` | `8.2rem` | 50 | | `el-spacing.6xl` | `12.3rem` | 51 | 52 | ### Radius Tokens 53 | Spacing tokens are used to space out (usually large or large sets of) elements outside and inside. The spacing tokens borrow the ** 54 | base-8** approach used by GitHub in their [Primer Design System](https://primer.style/css/support/spacing#spacing-scale) 55 | . 56 | 57 | | Token | Value | 58 | |---------------|----------| 59 | | `radius.none` | `0` | 60 | | `radius.xs` | `0.1rem` | 61 | | `radius.sm` | `0.3rem` | 62 | | `radius.md` | `0.5rem` | 63 | | `radius.lg` | `0.7rem` | 64 | | `radius.xl` | `1.2rem` | 65 | | `radius.2xl` | `2.3rem` | 66 | | `radius.3xl` | `3.4rem` | 67 | | `radius.4xl` | `4.5rem` | 68 | | `radius.5xl` | `6.1rem` | 69 | | `radius.6xl` | `8.2rem` | 70 | 71 | The radius tokens have a few special token variants: 72 | 73 | | Token | Value | 74 | |-----------------|----------| 75 | | `radius.pill` | `5000px` | 76 | | `radius.circle` | `50%` | 77 | 78 | ### Dimension Tokens 79 | Dimension tokens are applied to anything that defines the width and height of an element. We recommend only using these 80 | for max and min values to preserve the responsiveness. 81 | 82 | | Token | Value | 83 | |------------------|----------| 84 | | `dimension.none` | `0` | 85 | | `dimension.xs` | `277px` | 86 | | `dimension.sm` | `320px` | 87 | | `dimension.md` | `640px` | 88 | | `dimension.lg` | `890px` | 89 | | `dimension.xl` | `1077px` | 90 | 91 | We've implemented a special dimension token for [item sprites](../sprites.md). 92 | 93 | | Token | Value | 94 | |--------------------|--------| 95 | | `dimension.sprite` | `32px` | 96 | 97 | ### Border Tokens 98 | Border tokens define the size of the edges of an element. 99 | 100 | | Token | Value | 101 | |---------------|--------| 102 | | `border.none` | `0` | 103 | | `border.xs` | `1px` | 104 | | `border.sm` | `3px` | 105 | | `border.md` | `5px` | 106 | | `border.lg` | `7x` | 107 | | `border.xl` | `11px` | -------------------------------------------------------------------------------- /specs/api/composition/color.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Color Composition API 3 | slug: /api/composition/color 4 | --- 5 | # Color Composition API 6 | ## Apply Mixin 7 | This mixin is located inside the main `Himig` module. This mixin allows you to apply colors based on Himig's component 8 | color anatomy spec, and creating an API with it. 9 | 10 | ```scss 11 | @use 'pkg:@matteusan/himig'; 12 | 13 | .hs-component { 14 | @include himig.color-apply('component', ( 15 | fill: '', 16 | ink: '', 17 | border: '' 18 | )); 19 | } 20 | ``` 21 | 22 | #### Syntax 23 | 24 | ```scss 25 | @mixin color-apply($component, $theme) { 26 | ... 27 | } 28 | ``` 29 | 30 | | Parameter | Type | Description | 31 | |------------|-----------------------|---------------------------------------------------------| 32 | | $component | `string` | The component name. This is used for the keys. | 33 | | $theme | `map` | The colors you want to apply. (`fill`, `ink`, `border`) | 34 | 35 | ## Bind Mixin 36 | This mixin is located inside the main `Himig` module. This mixin allows you to bind colors based on Himig's component 37 | color anatomy spec to an existing styled component (with the appropriate keys). 38 | 39 | This is especially useful whenever you want to make variants in color on an already styled component. 40 | 41 | ```scss 42 | @use 'pkg:@matteusan/himig'; 43 | 44 | .hs-component { 45 | @include himig.color-bind('component', ( 46 | fill: '', 47 | ink: '', 48 | border: '' 49 | )); 50 | } 51 | ``` 52 | 53 | #### Syntax 54 | 55 | ```scss 56 | @mixin color-bind($component, $theme) { 57 | ... 58 | } 59 | ``` 60 | 61 | | Parameter | Type | Description | 62 | |------------|-----------------------|---------------------------------------------------------| 63 | | $component | `string` | The component name. This is used for the keys. | 64 | | $theme | `map` | The colors you want to apply. (`fill`, `ink`, `border`) | 65 | 66 | ## Utility Mixins 67 | These mixins are used internally by the `apply` and `bind` mixins, but you can also use them independently to apply specific color styles. 68 | 69 | ### Border Mixin 70 | 71 | ```scss 72 | @use 'pkg:@matteusan/himig'; 73 | 74 | .hs-component { 75 | @include himig.color-border('component', 'primary-400'); 76 | } 77 | ``` 78 | 79 | | Parameter | Type | Description | 80 | |------------|------------------|-----------------------------------------------------------------------------| 81 | | $component | `string` | The component name. This is used for the keys. | 82 | | $color | `token or color` | The border color you want to apply. | 83 | | $intent | `string` | The intention of the color application. Choose between `create` and `bind`. | 84 | 85 | ## Fill Mixin 86 | 87 | ```scss 88 | @use 'pkg:@matteusan/himig'; 89 | 90 | .hs-component { 91 | @include himig.color-fill('component', 'primary-400'); 92 | } 93 | ``` 94 | 95 | | Parameter | Type | Description | 96 | |------------|------------------|-----------------------------------------------------------------------------| 97 | | $component | `string` | The component name. This is used for the keys. | 98 | | $color | `token or color` | The fill color you want to apply. | 99 | | $intent | `string` | The intention of the color application. Choose between `create` and `bind`. | 100 | 101 | ## Ink Mixin 102 | 103 | ```scss 104 | @use 'pkg:@matteusan/himig'; 105 | 106 | .hs-component { 107 | @include himig.color-ink('component', 'primary-400'); 108 | } 109 | ``` 110 | 111 | | Parameter | Type | Description | 112 | |------------|------------------|-----------------------------------------------------------------------------| 113 | | $component | `string` | The component name. This is used for the keys. | 114 | | $color | `token or color` | The text color you want to apply. | 115 | | $intent | `string` | The intention of the color application. Choose between `create` and `bind`. | -------------------------------------------------------------------------------- /specs/foundation/theme.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Theme 3 | slug: /foundation/theme 4 | --- 5 | # Theme 6 | The theme allows you to organize tokens in a more manageable way. It prepares the tokens for component use as the raw tokens 7 | are commonly not allowed for component use. 8 | 9 | The theme governs three aspects of the design system: Color, structure, and typography. 10 | 11 | ## Color Theming 12 | The color theme is defined in four different parts: 13 | 14 | 1) **Primary Color** - Defines the color of most of the medium-sized surfaces like cards, profiles, lists, etc. 15 | 2) **Accent Color** - Defines the color of elements that require attention like a button, alert, badge, etc. 16 | 3) **Surface Colors** - Defines the color of most of the large-sized surfaces like overviews, headers, appbars, navigation bars etc. 17 | 4) **Background Color** - Defines the color of the surface all elements will interact on. 18 | 5) **State Colors** - Defines the color of surfaces that indicate a state like error, warning, and success. 19 | 20 | #### SCSS 21 | Typically, when setting this up the design system comes in with the GS Theme by default. To override these, you can use 22 | a [color token key](color/color-tokens.md) to change the color. Otherwise, if you wish to use raw color values, you'd 23 | have to pass in a `map` with the [`100-800` schema](color/color-tokens.md#color-theme-schema). 24 | 25 | | $parameter | Type | 26 | |---------------|------------------------| 27 | | `$primary` | Color Token Key or Map | 28 | | `$accent` | Color Token Key or Map | 29 | | `$surface` | Color Token Key or Map | 30 | | `$background` | Color Token | 31 | | `$error` | Color Token Key or Map | 32 | 33 | ```scss 34 | @use 'pkg:@matteusan/sentro'; 35 | 36 | @include himig.init( 37 | $surface: ( 38 | 100: #f70, 39 | 200: #f70, 40 | 300: #f70, 41 | 400: #f70, 42 | 500: #f70, 43 | 600: #f70, 44 | 700: #f70, 45 | 800: #f70, 46 | ), 47 | $accent: 'ms-yellow', 48 | $background: 'ms-green.800', 49 | ) { 50 | // Components... 51 | } 52 | ``` 53 | 54 | ## Structure Theming 55 | The structure theme is divided into different portions: 56 | 57 | 1) **Radius** - Defines the corner radii of an element. 58 | 2) **Breakpoints** - Defines points on the screen where certain styles can happen. 59 | 3) **Dimensions** - Defines the width and height of an element. 60 | 61 | #### SCSS 62 | Typically, when setting this up the design system comes in with the GS Theme by default. To override these, pass in a 63 | map, insert a theme variant, and use 64 | a [structure token key](structure/structure-tokens.md) to change the value, or pass in a raw value if you want something 65 | more custom. 66 | 67 | | $parameter | Type | 68 | |----------------|--------------------------------------| 69 | | `$radius` | Map of Structure Tokens or raw value | 70 | | `$breakpoints` | Map of Structure Tokens or raw value | 71 | 72 | ```scss 73 | @use 'pkg:@matteusan/sentro'; 74 | 75 | @include himig.init( 76 | $radius: ( 77 | small: 'radius.sm', 78 | medium: 'radius.md', 79 | large: 'radius.lg', 80 | ), 81 | $breakpoints: ( 82 | small: 324px, 83 | medium: 'breakpoint.md', 84 | large: 'breakpoint.lg' 85 | ) 86 | ) { 87 | // Components... 88 | } 89 | ``` 90 | 91 | ## Typography Theming 92 | Typography Theming is divided by purpose: 93 | 94 | 1) **Global** - Defines the entirety of the typography theme 95 | 2) **Supertitle** - For large headings, commonly used in hero components. 96 | 3) **Title** - For headings. This defines a section of the site. 97 | 4) **Subtitle** - For subheadings. This defines a sub section of a section. 98 | 5) **Body** - The default text style. Applies to most content. 99 | 6) **Mono** - The default mono text style. Applies to code snippets, especially in pages that displays logs and metadata. 100 | 7) **Small** - For small texts. Applies to content that are minor in hierarchy. 101 | 102 | #### SCSS 103 | Typically, when setting this up the design system comes in with the GS Theme by default. To override these, pass in a 104 | map, insert your desired property, and use 105 | a [typography token key](typography/typography-tokens.md) to change the value, or pass in a raw value if you want 106 | something more custom. 107 | 108 | ```scss 109 | @use 'pkg:@matteusan/sentro'; 110 | 111 | @include himig.init( 112 | $global: ( 113 | family: 'family.sans-serif', 114 | size: 'size.sm', 115 | weight: 'weight.normal', 116 | line-height: 'line-height.normal' 117 | ), 118 | $supertitle: ( 119 | family: ('Open Sans', sans-serif), 120 | size: 'size.2xl', 121 | weight: 'weight.black' 122 | ), 123 | ) { 124 | // Components... 125 | } 126 | ``` -------------------------------------------------------------------------------- /packages/himig/ms-tick-field/_tick-field-styles.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:list'; 7 | @use 'tick-field-base' as base; 8 | @use 'tick-field-theme' as theme; 9 | 10 | @mixin render( 11 | $color: (), 12 | $struct: (), 13 | $typography: (), 14 | $settings: () 15 | ) { 16 | $color: pintig.theme-merge(theme.$init-color, $color); 17 | $struct: pintig.theme-merge(theme.$init-struct, $struct); 18 | $typography: pintig.theme-merge(theme.$init-typography, $typography); 19 | $settings: pintig.theme-merge(theme.$init-settings, $settings); 20 | 21 | @layer components { 22 | @include base.init() { 23 | @include pintig.color-apply('tick-field', ( 24 | fill: pintig.theme-get($color, 'fill', 'disabled'), 25 | ink: pintig.theme-get($color, 'ink', 'disabled'), 26 | border: pintig.theme-get($color, 'border', 'disabled'), 27 | )); 28 | @include pintig.struct-apply('tick-field', $struct); 29 | @include pintig.typography-bind('tick-field', $typography); 30 | @include pintig.typography-bind('tick-field-helper', ( 31 | family: pintig.theme-get($typography, 'family'), 32 | size: pintig.theme-get($typography, 'size', 'helper'), 33 | weight: pintig.theme-get($typography, 'weight'), 34 | line-height: pintig.theme-get($typography, 'line-height'), 35 | )); 36 | cursor: pintig.key-create('tick-field-cursor', not-allowed); 37 | 38 | @include pintig.struct-bind('tick-field-text', ( 39 | gap: pintig.theme-get($struct, 'gap', 'text'), 40 | )); 41 | 42 | &:has(.ms-tick-field__input:not(:disabled)) { 43 | @include pintig.color-bind('tick-field', ( 44 | fill: pintig.theme-get($color, 'fill'), 45 | ink: pintig.theme-get($color, 'ink'), 46 | border: pintig.theme-get($color, 'border'), 47 | )); 48 | @include pintig.key-bind('tick-field-cursor', pointer); 49 | 50 | &:hover, 51 | &:has(.ms-tick-field__input:focus) { 52 | @include pintig.color-bind('tick-field', ( 53 | fill: rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'hover')) r g b / pintig.$color-fill-focus-opacity), 54 | border: rgb(from pintig.token-switch(pintig.theme-get($color, 'border', 'hover')) r g b / pintig.$color-fill-focus-opacity), 55 | )); 56 | } 57 | 58 | &:has(.ms-tick-field__input:checked) { 59 | @include pintig.color-bind('tick-field', ( 60 | fill: rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'checked')) r g b / pintig.$color-fill-focus-opacity), 61 | border: pintig.theme-get($color, 'border', 'checked'), 62 | )); 63 | } 64 | } 65 | 66 | .ms-tick-field__input { 67 | @include pintig.struct-apply('tick-field-input', ( 68 | width: 1.5rem, 69 | height: 1.5rem, 70 | margin: 0, 71 | border-width: 'sm', 72 | border-style: 'solid' 73 | )); 74 | @include pintig.color-bind('tick-field-input', ( 75 | fill: pintig.theme-get($color, 'fill', 'input', 'disabled'), 76 | ink: pintig.theme-get($color, 'ink', 'input'), 77 | border: pintig.theme-get($color, 'border', 'input', 'disabled'), 78 | )); 79 | 80 | &:not(:disabled) { 81 | @include pintig.color-bind('tick-field-input', ( 82 | fill: pintig.theme-get($color, 'fill', 'input'), 83 | ink: pintig.theme-get($color, 'ink', 'input'), 84 | border: pintig.theme-get($color, 'border', 'input'), 85 | )); 86 | 87 | &:hover, 88 | &:focus { 89 | @include pintig.color-fill('tick-field-input', rgb(from pintig.token-switch(pintig.theme-get($color, 'fill', 'input', 'hover')) r g b / pintig.$color-fill-focus-opacity), 'bind'); 90 | @include pintig.color-border('tick-field-input', rgb(from pintig.token-switch(pintig.theme-get($color, 'border', 'input', 'hover')) r g b / pintig.$color-fill-focus-opacity), 'bind'); 91 | } 92 | 93 | &:checked { 94 | @include pintig.color-bind('tick-field-input', ( 95 | fill: pintig.theme-get($color, 'fill', 'input', 'checked'), 96 | ink: pintig.theme-get($color, 'ink', 'input'), 97 | border: pintig.theme-get($color, 'border', 'input', 'checked'), 98 | )); 99 | } 100 | } 101 | 102 | &[type='checkbox'] { 103 | @include pintig.struct-radius('tick-field-input', pintig.theme-get($struct, 'radius', 'input', 'checkbox'), 'bind'); 104 | } 105 | 106 | &[type='radio'] { 107 | @include pintig.struct-radius('tick-field-input', pintig.theme-get($struct, 'radius', 'input', 'radio'), 'bind'); 108 | } 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /packages/himig/ms-button/_button-mixins.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use 'pkg:@matteusan/pintig'; 6 | @use 'sass:map'; 7 | @use 'sass:string'; 8 | 9 | @mixin text($fills, $inks, $borders) { 10 | $fill: map.get($fills, 'default'); 11 | $ink-disabled: map.get($inks, 'disabled'); 12 | $border: map.get($borders, 'default'); 13 | 14 | &:not(:disabled) { 15 | @include pintig.color-bind('button', ( 16 | fill: transparent, 17 | ink: $fill, 18 | border: transparent 19 | )); 20 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-fill-default-opacity)); 21 | 22 | &:hover { 23 | @include pintig.color-bind('button', ( 24 | fill: _alpha($fill, pintig.$color-fill-hover-opacity), 25 | ink: $fill, 26 | border: _alpha($border, pintig.$color-border-hover-opacity, $type: 'border') 27 | )); 28 | } 29 | 30 | &:focus { 31 | @include pintig.color-bind('button', ( 32 | fill: _alpha($fill, pintig.$color-fill-focus-opacity), 33 | ink: $fill, 34 | border: _alpha($border, pintig.$color-border-focus-opacity, $type: 'border') 35 | )); 36 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-outline-focus-opacity)); 37 | } 38 | 39 | &:active { 40 | @include pintig.color-bind('button', ( 41 | fill: _alpha($fill, pintig.$color-fill-active-opacity), 42 | ink: $fill, 43 | border: _alpha($border, pintig.$color-border-active-opacity, $type: 'border') 44 | )); 45 | } 46 | } 47 | 48 | &:disabled { 49 | @include pintig.color-bind('button', ( 50 | fill: transparent, 51 | ink: $ink-disabled, 52 | border: transparent 53 | )); 54 | } 55 | } 56 | 57 | @mixin outlined($fills, $inks, $borders) { 58 | $fill: map.get($fills, 'default'); 59 | $fill-hover: map.get($fills, 'hover'); 60 | $fill-focus: map.get($fills, 'focus'); 61 | $fill-active: map.get($fills, 'active'); 62 | $fill-disabled: map.get($fills, 'disabled'); 63 | $ink: map.get($inks, 'default'); 64 | $ink-disabled: map.get($inks, 'disabled'); 65 | $border: map.get($borders, 'default'); 66 | $border-hover: map.get($borders, 'hover'); 67 | $border-focus: map.get($borders, 'focus'); 68 | $border-active: map.get($borders, 'active'); 69 | $border-disabled: map.get($borders, 'disabled'); 70 | 71 | &:not(:disabled) { 72 | @include pintig.color-bind('button', ( 73 | fill: transparent, 74 | ink: $fill, 75 | border: $border 76 | )); 77 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-fill-default-opacity)); 78 | 79 | &:focus { 80 | @include pintig.color-bind('button', ( 81 | fill: _alpha($fill, pintig.$color-fill-focus-opacity), 82 | ink: $fill, 83 | border: $border 84 | )); 85 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-outline-focus-opacity)); 86 | } 87 | 88 | &:hover { 89 | @include pintig.color-bind('button', ( 90 | fill: $fill-hover, 91 | ink: $ink, 92 | border: $border-hover 93 | )); 94 | } 95 | 96 | &:active { 97 | @include pintig.color-bind('button', ( 98 | fill: $fill-active, 99 | ink: $ink, 100 | border: $border-active 101 | )); 102 | } 103 | } 104 | 105 | &:disabled { 106 | @include pintig.color-bind('button', ( 107 | fill: transparent, 108 | ink: $ink-disabled, 109 | border: $border-disabled 110 | )); 111 | } 112 | } 113 | 114 | @mixin filled($fills, $inks, $borders) { 115 | $fill: map.get($fills, 'default'); 116 | $fill-hover: map.get($fills, 'hover'); 117 | $fill-focus: map.get($fills, 'focus'); 118 | $fill-active: map.get($fills, 'active'); 119 | $fill-disabled: map.get($fills, 'disabled'); 120 | $ink: map.get($inks, 'default'); 121 | $ink-disabled: map.get($inks, 'disabled'); 122 | $border: map.get($borders, 'default'); 123 | $border-hover: map.get($borders, 'hover'); 124 | $border-focus: map.get($borders, 'focus'); 125 | $border-active: map.get($borders, 'active'); 126 | $border-disabled: map.get($borders, 'disabled'); 127 | 128 | &:not(:disabled) { 129 | @include pintig.color-bind('button', ( 130 | fill: $fill, 131 | ink: $ink, 132 | border: $border 133 | )); 134 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-fill-default-opacity)); 135 | 136 | &:hover { 137 | @include pintig.color-bind('button', ( 138 | fill: $fill-hover, 139 | ink: $ink, 140 | border: $border-hover 141 | )); 142 | } 143 | 144 | &:focus { 145 | @include pintig.color-bind('button', ( 146 | fill: $fill-focus, 147 | ink: $ink, 148 | border: $border-focus 149 | )); 150 | @include pintig.key-bind('button-outline', 2px solid rgb(from _raw($fill) r g b / pintig.$color-outline-focus-opacity)); 151 | } 152 | 153 | &:active { 154 | @include pintig.color-bind('button', ( 155 | fill: $fill-active, 156 | ink: $ink, 157 | border: $border-active 158 | )); 159 | } 160 | } 161 | 162 | &:disabled { 163 | @include pintig.color-bind('button', ( 164 | fill: $fill-disabled, 165 | ink: $ink-disabled, 166 | border: $border-disabled 167 | )); 168 | } 169 | } 170 | 171 | @function _alpha($query, $opacity, $type: 'fill', $state: 'hover') { 172 | @return rgb(from _raw($query) r g b / $opacity); 173 | } 174 | 175 | @function _raw($query) { 176 | @return pintig.token-switch($query); 177 | } -------------------------------------------------------------------------------- /specs/components/ms-button.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MsButton 3 | slug: /components/ms-button 4 | --- 5 | # MsButton 6 | Buttons call the user to a specific action in the page. May it be in a form, or a hyperlink. 7 | 8 | ## Usage 9 | ### HTML 10 | ```html 11 | 14 | ``` 15 | There's a caveat to the HTML structure. If you wish to use the button component to redirect a user to another page, please use `` tags instead. This improves accessibility, and the way styles work for this component. 16 | 17 | Additionally, anchor elements don't have a disabled attribute so you wouldn't really need a disabled state for this. 18 | ```html 19 | 20 | Button 21 | 22 | ``` 23 | 24 | ### SCSS 25 | ```scss 26 | @use 'pkg:@matteusan/sentro'; 27 | @use '@matteusan/himig/ms-button'; 28 | 29 | @include himig.init() { 30 | @include ms-button.render(); 31 | } 32 | ``` 33 | 34 | ## Variants 35 | Button variants are called using the [`is-` modifier](../foundation/components.md#structure). 36 | 37 | ### Text Variant 38 | These are used as tertiary actions, or actions that are of the least attention to. 39 | ```html 40 | 43 | ``` 44 | 45 | ### Outlined Variant 46 | These are used as secondary actions, or actions that are of the second of the attention to. 47 | ```html 48 | 51 | ``` 52 | 53 | ### Filled Variant 54 | These are used as primary actions, or actions that are of the first of the attention to. 55 | ```html 56 | 59 | ``` 60 | 61 | ## Icons 62 | Icons are great for attaching pictograms to actions. This makes repeated actions more memorable and easier to understand at a glance. Either can be placed as a leading or trailing icon; but never both at the same time. 63 | 64 | ### Leading Icon 65 | ```html 66 | 70 | ``` 71 | 72 | ### Trailing Icon 73 | ```html 74 | 78 | ``` 79 | 80 | ## API 81 | ### Anatomical Classes 82 | These classes make up the elements inside a component. 83 | 84 | | Class | Effect | 85 | |---------------------|-----------------------------| 86 | | `.ms-button` | Main button class. | 87 | | `.ms-button__label` | Class for the button label. | 88 | | `.ms-button__icon` | Class for the button icon. | 89 | 90 | ### Variant Classes 91 | For information on how to use these classes, [click here](index.md#modification-html). 92 | 93 | | Class | Effect | 94 | |-----------------|-------------------------------------------------------| 95 | | `.is-outlined` | Renders the component in its outlined style. | 96 | | `.is-filled` | Renders the component in its filled style. | 97 | | `.is-error` | Renders the component in its error state. | 98 | 99 | 100 | ### Custom Properties 101 | These are for creating your own component theme classes that you can append to the parent element markup. 102 | 103 | | Property | Effect | 104 | |--------------------------|------------------------------------------| 105 | | `--ms-button-fill` | Changes the button's background color. | 106 | | `--ms-button-ink` | Changes the button's text color. | 107 | | `--ms-button-border` | Changes the button's border color. | 108 | | `--ms-button-radius` | Changes the button's border radius. | 109 | | `--ms-button-gap` | Changes the button's label and icon gap. | 110 | | `--ms-button-padding` | Changes the button's padding. | 111 | | `--ms-button-weight` | Changes the button's weight. | 112 | | `--ms-button-icon-ink` | Changes the button's icon color. | 113 | | `--ms-button-icon-size` | Changes the button's icon size. | 114 | | `--ms-button-label-ink` | Changes the button's label color. | 115 | | `--ms-button-label-size` | Changes the button's label size. | 116 | 117 | ### Configuring styles 118 | Here are all the themeable properties for this component. The directions to use these properties are located in 119 | the [render API](index.md#modification-scss). 120 | 121 | ```scss 122 | // _button-theme.scss 123 | 124 | $init-color: ( 125 | fill: ( 126 | default: 'accent-400', 127 | hover: 'accent-300', 128 | active: 'accent-200', 129 | focus: 'accent-400', 130 | disabled: 'disabled', 131 | ), 132 | ink: ( 133 | default: 'accent-ink', 134 | hover: 'accent-ink', 135 | active: 'accent-ink', 136 | focus: 'accent-ink', 137 | disabled: 'disabled-ink', 138 | ), 139 | border: ( 140 | default: 'accent-400', 141 | hover: 'accent-300', 142 | active: 'accent-200', 143 | focus: 'accent-400', 144 | disabled: 'disabled', 145 | ), 146 | ); 147 | 148 | $init-struct: ( 149 | width: ( 150 | default: 100%, 151 | min: 69px, 152 | max: max-content 153 | ), 154 | padding: ('md' 'lg'), 155 | radius: 'small', 156 | border-width: 'xs', 157 | border-style: solid, 158 | gap: 'xs' 159 | ); 160 | 161 | $init-typography: ( 162 | family: 'global', 163 | size: 'body', 164 | weight: 'bold', 165 | line-height: 1.25rem 166 | ); 167 | 168 | $init-settings: (); 169 | ``` 170 | 171 | ### Extending styles 172 | If you wish to extend the component styles, the [`extend()` API](index.md#extension-scss) might come in handy. -------------------------------------------------------------------------------- /packages/pintig/struct/_struct-utils.scss: -------------------------------------------------------------------------------- 1 | // @license 2 | // Copyright (c) 2025 MatteuSan 3 | // SPDX-License-Identifier: MIT 4 | 5 | @use '../theme'; 6 | @use '../tokens'; 7 | @use '../tools'; 8 | @use 'sass:map'; 9 | @use 'sass:list'; 10 | 11 | $_valid-arbitrary-values: (0, inherit); 12 | 13 | /// Mixin for applying width. 14 | /// @param {string} $component 15 | /// @param {map|number|width} $width 16 | /// @param {string} $intent 17 | /// @return {void} width styles. 18 | @mixin width($component, $width, $intent: 'create') { 19 | @if tools.is-type($width, 'map', $errors: false) { 20 | $min: map.get($width, 'min'); 21 | $default: map.get($width, 'default'); 22 | $max: map.get($width, 'max'); 23 | 24 | @if $min { 25 | @include theme.property( 26 | min-width, 27 | ('#{$component}-min-width', theme.token-switch('width.#{$min}', $min)), 28 | $intent 29 | ); 30 | } 31 | @if $default { 32 | @include theme.property( 33 | width, 34 | ('#{$component}-width', theme.token-switch('width.#{$default}', $default)), 35 | $intent 36 | ); 37 | } 38 | @if $max { 39 | @include theme.property( 40 | max-width, 41 | ('#{$component}-max-width', theme.token-switch('width.#{$max}', $max)), 42 | $intent 43 | ); 44 | } 45 | } @else { 46 | @include theme.property( 47 | width, 48 | ('#{$component}-width', theme.token-switch('width.#{$width}', $width)), 49 | $intent 50 | ); 51 | } 52 | } 53 | 54 | /// Mixin for applying height. 55 | /// @param {string} $component 56 | /// @param {map|number|height} $height 57 | /// @param {string} $intent 58 | /// @return {void} width styles. 59 | @mixin height($component, $height, $intent: 'create') { 60 | @if tools.is-type($height, 'map', $errors: false) { 61 | $min: map.get($height, 'min'); 62 | $default: map.get($height, 'default'); 63 | $max: map.get($height, 'max'); 64 | 65 | @if $min { 66 | @include theme.property( 67 | min-height, 68 | ('#{$component}-min-height', theme.token-switch('height.#{$min}', $min)), 69 | $intent 70 | ); 71 | } 72 | @if $default { 73 | @include theme.property( 74 | height, 75 | ('#{$component}-height', theme.token-switch('height.#{$default}', $default)), 76 | $intent 77 | ); 78 | } 79 | @if $max { 80 | @include theme.property( 81 | max-height, 82 | ('#{$component}-max-height', theme.token-switch('height.#{$max}', $max)), 83 | $intent 84 | ); 85 | } 86 | } @else { 87 | @include theme.property( 88 | height, 89 | ('#{$component}-height', theme.token-switch('height.#{$height}', $height)), 90 | $intent 91 | ); 92 | } 93 | } 94 | 95 | /// Mixin for applying radii. 96 | /// @param {string} $component 97 | /// @param {list|token|number} $radius 98 | /// @param {string} $intent 99 | /// @return {void} radii styles. 100 | @mixin radius($component, $radius, $intent: 'create') { 101 | @include theme.property( 102 | border-radius, 103 | ('#{$component}-radius', _parse-value('radius', $radius)), 104 | $intent 105 | ); 106 | } 107 | 108 | /// Mixin for applying padding. 109 | /// @param {string} $component 110 | /// @param {list|token|number} $padding 111 | /// @param {string} $intent 112 | /// @return {void} padding styles. 113 | @mixin padding($component, $padding, $intent: 'create') { 114 | @include theme.property( 115 | padding, 116 | ('#{$component}-padding', _parse-value('padding', $padding)), 117 | $intent 118 | ); 119 | } 120 | 121 | /// Mixin for applying margin. 122 | /// @param {string} $component 123 | /// @param {list|token|number} $margin 124 | /// @param {string} $intent 125 | /// @return {void} margin styles. 126 | @mixin margin($component, $margin, $intent: 'create') { 127 | @include theme.property( 128 | margin, 129 | ('#{$component}-margin', _parse-value('margin', $margin)), 130 | $intent 131 | ); 132 | } 133 | 134 | /// Mixin for applying border width. 135 | /// @param {string} $component 136 | /// @param {token|number} $border-width 137 | /// @param {string} $intent 138 | /// @return {void} border width styles. 139 | @mixin border-width($component, $border-width, $intent: 'create') { 140 | @include theme.property( 141 | border-width, 142 | ('#{$component}-border-width', _parse-value('border', $border-width)), 143 | $intent 144 | ); 145 | } 146 | 147 | /// Mixin for applying border style. 148 | /// @param {string} $component 149 | /// @param {string} $border-style 150 | /// @param {string} $intent 151 | /// @return {void} border style styles. 152 | @mixin border-style($component, $border-style, $intent: 'create') { 153 | @include theme.property( 154 | border-style, 155 | ('#{$component}-border-style', $border-style), 156 | $intent 157 | ); 158 | } 159 | 160 | /// Mixin for applying shadow. 161 | /// @param {string} $component 162 | /// @param {token} $shadow 163 | /// @param {string} $intent 164 | /// @return {void} shadow styles. 165 | @mixin shadow($component, $shadow, $intent: 'create') { 166 | @include theme.property( 167 | box-shadow, 168 | ('#{$component}-shadow', theme.token-switch('shadow-#{$shadow}', tokens.primitive-token-switch('shadow.#{$shadow}', $shadow))), 169 | $intent 170 | ); 171 | } 172 | 173 | /// Mixin for applying gap. 174 | /// @param {string} $component 175 | /// @param {token} $gap 176 | /// @param {string} $intent 177 | /// @return {void} gap styles. 178 | @mixin gap($component, $gap, $intent: 'create') { 179 | @include theme.property( 180 | gap, 181 | ('#{$component}-gap', theme.token-switch('gap-#{$gap}', tokens.primitive-token-switch('gap.#{$gap}', $gap))), 182 | $intent 183 | ); 184 | } 185 | 186 | /// Value parser for the shape setter mixins. 187 | /// @access private 188 | @function _parse-value($key, $query) { 189 | $result: (); 190 | @each $value in $query { 191 | @if theme.token-check('#{$key}-#{$value}') or tokens.primitive-token-check('#{$key}.#{$value}') { 192 | $result: list.append($result, 193 | theme.token-switch('#{$key}-#{$value}', tokens.primitive-token-switch('#{$key}.#{$value}', $value)), 194 | 'space'); 195 | } @else { 196 | $result: list.append($result, $value, 'space'); 197 | } 198 | } 199 | @return $result; 200 | } --------------------------------------------------------------------------------