├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src ├── app.html ├── global.d.ts ├── lib │ ├── AutoSuggestBox │ │ ├── AutoSuggestBox.scss │ │ └── AutoSuggestBox.svelte │ ├── Button │ │ ├── Button.scss │ │ └── Button.svelte │ ├── CalendarDatePicker │ │ ├── CalendarDatePicker.scss │ │ └── CalendarDatePicker.svelte │ ├── CalendarView │ │ ├── CalendarView.scss │ │ ├── CalendarView.svelte │ │ ├── CalendarViewItem.scss │ │ └── CalendarViewItem.svelte │ ├── Checkbox │ │ ├── Checkbox.scss │ │ └── Checkbox.svelte │ ├── ComboBox │ │ ├── ComboBox.scss │ │ ├── ComboBox.svelte │ │ ├── ComboBoxItem.scss │ │ └── ComboBoxItem.svelte │ ├── ContentDialog │ │ ├── ContentDialog.scss │ │ └── ContentDialog.svelte │ ├── ContextMenu │ │ ├── ContextMenu.scss │ │ └── ContextMenu.svelte │ ├── Expander │ │ ├── Expander.scss │ │ └── Expander.svelte │ ├── Flyout │ │ ├── FlyoutSurface.scss │ │ ├── FlyoutSurface.svelte │ │ ├── FlyoutWrapper.scss │ │ └── FlyoutWrapper.svelte │ ├── IconButton │ │ ├── IconButton.scss │ │ └── IconButton.svelte │ ├── InfoBadge │ │ ├── InfoBadge.scss │ │ └── InfoBadge.svelte │ ├── InfoBar │ │ ├── InfoBar.scss │ │ └── InfoBar.svelte │ ├── ListItem │ │ ├── ListItem.scss │ │ └── ListItem.svelte │ ├── MenuBar │ │ ├── MenuBar.scss │ │ ├── MenuBar.svelte │ │ ├── MenuBarItem.scss │ │ ├── MenuBarItem.svelte │ │ └── flyoutState.ts │ ├── MenuFlyout │ │ ├── MenuFlyoutDivider.scss │ │ ├── MenuFlyoutDivider.svelte │ │ ├── MenuFlyoutItem.scss │ │ ├── MenuFlyoutItem.svelte │ │ ├── MenuFlyoutSurface.scss │ │ ├── MenuFlyoutSurface.svelte │ │ ├── MenuFlyoutWrapper.scss │ │ └── MenuFlyoutWrapper.svelte │ ├── NavigationView │ │ ├── NavigationView.scss │ │ └── NavigationView.svelte │ ├── NumberBox │ │ ├── NumberBox.scss │ │ └── NumberBox.svelte │ ├── PersonPicture │ │ ├── PersonPicture.scss │ │ └── PersonPicture.svelte │ ├── ProgressBar │ │ ├── ProgressBar.scss │ │ └── ProgressBar.svelte │ ├── ProgressRing │ │ ├── ProgressRing.scss │ │ └── ProgressRing.svelte │ ├── RadioButton │ │ ├── RadioButton.scss │ │ └── RadioButton.svelte │ ├── ScrollView │ │ └── ScrollView.svelte │ ├── Slider │ │ ├── Slider.scss │ │ └── Slider.svelte │ ├── TextBlock │ │ ├── TextBlock.scss │ │ └── TextBlock.svelte │ ├── TextBox │ │ ├── TextBox.scss │ │ ├── TextBox.svelte │ │ ├── TextBoxButton.scss │ │ └── TextBoxButton.svelte │ ├── ToggleSwitch │ │ ├── ToggleSwitch.scss │ │ └── ToggleSwitch.svelte │ ├── Tooltip │ │ ├── TooltipSurface.scss │ │ ├── TooltipSurface.svelte │ │ ├── TooltipWrapper.scss │ │ └── TooltipWrapper.svelte │ ├── _mixins.scss │ ├── index.ts │ ├── internal.ts │ ├── svelte-jsx.d.ts │ └── theme.css ├── routes │ ├── __layout.svelte │ ├── docs │ │ ├── __layout.svelte │ │ ├── components │ │ │ ├── button.md │ │ │ ├── calendarview.md │ │ │ ├── checkbox.md │ │ │ ├── contentdialog.md │ │ │ ├── expander.md │ │ │ ├── flyout.md │ │ │ ├── iconbutton.md │ │ │ ├── infobadge.md │ │ │ ├── infobar.md │ │ │ ├── listitem.md │ │ │ ├── personpicture.md │ │ │ ├── progressring.md │ │ │ ├── radiobutton.md │ │ │ ├── slider.md │ │ │ ├── textblock.md │ │ │ ├── textbox.md │ │ │ └── toggleswitch.md │ │ ├── getting-started.md │ │ ├── index.md │ │ └── internals │ │ │ └── index.md │ ├── index.svelte │ └── test │ │ ├── __layout-test.svelte │ │ ├── index.svelte │ │ └── nav.svelte └── site │ ├── data │ ├── docs.ts │ └── home.ts │ ├── lib │ ├── APIDocs │ │ ├── APIDocs.svelte │ │ └── ParsedComponent.d.ts │ ├── CopyBox │ │ └── CopyBox.svelte │ ├── Example │ │ ├── Example.scss │ │ └── Example.svelte │ ├── HeroCard │ │ ├── HeroCard.scss │ │ └── HeroCard.svelte │ ├── Metadata │ │ └── Metadata.svelte │ ├── Navbar │ │ ├── Navbar.scss │ │ └── Navbar.svelte │ ├── PageSection │ │ ├── PageSection.scss │ │ └── PageSection.svelte │ ├── Showcase │ │ ├── Showcase.scss │ │ └── Showcase.svelte │ ├── Toc │ │ ├── Toc.scss │ │ └── Toc.svelte │ ├── TreeView │ │ └── TreeView.svelte │ └── index.ts │ └── styles │ ├── _markdown.scss │ ├── _mixins.scss │ ├── global.scss │ └── pages │ ├── docs.scss │ └── home.scss ├── static ├── bloom-mica-dark.png ├── bloom-mica-light.png └── logo.svg ├── svelte.config.js └── tsconfig.json /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a bug report to help improve the Fluent Svelte library 3 | labels: [bug] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Before you start... 8 | options: 9 | - label: Have you updated your dependencies? You might be using an old version of the library. 10 | required: true 11 | - label: Have you checked for an existing issue on this topic? If there is one, post a comment on it instead. 12 | required: true 13 | - type: dropdown 14 | id: browsers 15 | attributes: 16 | label: What browsers are you seeing the problem on? 17 | multiple: true 18 | options: 19 | - Firefox 20 | - Chrome 21 | - Safari 22 | - Microsoft Edge 23 | - Firefox Beta/Dev/Nightly 24 | - Chrome Beta/Dev/Canary 25 | - Safari Beta 26 | - Microsoft Edge Beta/Dev/Canary 27 | - Other - list in description 28 | validations: 29 | required: true 30 | - type: textarea 31 | attributes: 32 | label: Description 33 | description: A clear and concise description of what the bug is. 34 | validations: 35 | required: true 36 | - type: textarea 37 | attributes: 38 | label: Steps To Reproduce 39 | description: Steps to reproduce the behavior. 40 | placeholder: | 41 | 1. Go to '....' 42 | 2. Click on '....' 43 | 3. Scroll down to '....' 44 | 4. See the error 45 | validations: 46 | required: false 47 | - type: textarea 48 | attributes: 49 | label: Expected behavior 50 | description: A clear and concise description of what you expected to happen. 51 | validations: 52 | required: true 53 | - type: textarea 54 | attributes: 55 | label: Relevant Assets 56 | description: | 57 | A list of assets (errors, screenshots) relevant to this bug. 58 | 59 | Tip: You can attach images or log files by highlighting this area and dragging files into it. 60 | placeholder: | 61 | To access the site's log, press Ctrl + Shift + I and open the console tab. 62 | Errors will be highlighted in red. 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a new feature in the Fluent Svelte library. 3 | labels: [feature_request] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Before you start... 8 | options: 9 | - label: Have you updated your dependencies? You might be using an old version of the library. 10 | required: true 11 | - label: Have you checked if someone already requested this feature? If they have, post a comment with your use case instead. 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: Description 16 | description: A clear and concise description of what your idea is. Include things like possible use cases, drawbacks, etc. 17 | validations: 18 | required: true 19 | - type: textarea 20 | attributes: 21 | label: Alternative solutions 22 | description: Describe more ways this problem could be solved. 23 | validations: 24 | required: false 25 | - type: textarea 26 | attributes: 27 | label: Relevant Assets 28 | description: | 29 | A list of assets (screenshots, mockups) relevant to this feature request. 30 | 31 | Tip: You can attach images by highlighting this area and dragging files into it. 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | /.idea 7 | package-lock.json 8 | .vercel_build_output -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/routes/docs/** 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "trailingComma": "none", 5 | "arrowParens": "avoid", 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # fluent-svelte Changelog 2 | 3 | # 1.6.0 4 | - Corrected an issue with styling usage in `ProgressRing`. 5 | - Minor improvements and interaction fixes in `ComboBox`. 6 | - Exposed `` props to `CalendarDatePicker`. 7 | - Use `` in areas that are applicable. 8 | 9 | # 1.5.1 10 | 11 | - CalendarDatePicker now correctly resolves `../Flyout/FlyoutWrapper.svelte`. 12 | 13 | ## 1.5.0 14 | 15 | - Added `CalendarDatePicker` component. 16 | - CalendarView now forwards DOM events to it's outer container. 17 | - CalendarView pages will no longer start out of bounds if the first `value` provided is less than the `min` property or greater than the `max` property. 18 | - `change` events are now correctly fired from CalendarView. 19 | - Reverted a change that broke resolving internal components from `$lib/index.ts`. This mainly affects the REPL. 20 | - `className` on `Flyout` and `MenuFlyout` is now placed on the wrapper element rather than the flyout surface. 21 | - Fixed a minor bug where the user could momentarily click the `MenuFlyout` trigger button when closing the flyout, which would immediately reopen it. 22 | - The fly animation for `Flyout` is no longer tied to the surface element, making it present when the `override` slot is used. 23 | - Fixed a minor bug where the `CalendarView` table element would not appear until 150ms after it's initial rendering. 24 | - `MenuFlyout` and `ContextMenu` now correctly handle the Home and End keys when navigating items. 25 | - Updated the system font stack to include Helvetica. 26 | 27 | ## 1.4.1 28 | 29 | - **Breaking**: ComboBox now handles `value` differently. For more information, see [issue #25](https://github.com/Tropix126/fluent-svelte/issues/25). 30 | - Fixed a bug where `CalendarView` would throw an error if it wasn't initialized with a value. 31 | 32 | ## 1.4.0 33 | 34 | - Sliders now correctly dispatch `change` events. 35 | - Slider thumbs are now bound within the track contents, rather than overflowing by half their width. 36 | - Added `CalendarView` component. 37 | - Fallback font stack now includes `ui-sans-serif`, `-apple-system`, and `BlinkMacSystemFont`. 38 | 39 | ## Start of Changelog 40 | 41 | Past releases do not have a documented changelog. 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Fluent Svelte Team 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | Fluent Svelte 5 |

6 | 7 | > Warning: This project is still in alpha stages; I would not consider it production ready _yet_. Assume any undocumented component to be in the 0.x. phase of development and therefore unstable. 8 | 9 | ### What is this? 10 | 11 | `fluent-svelte` is an experimental [Svelte](http://svelte.dev/) component UI library that emulates the look and feel of [Microsoft's Windows UI Controls](https://github.com/microsoft/microsoft-ui-xaml/) which conform to the Fluent Design System. 12 | 13 | ### Features 14 | 15 | - [SvelteKit](https://kit.svelte.dev/) and SSR Compatible 16 | - [TypeScript](https://typescriptlang.org/) and type definitions are supported, but optional. 17 | - Full RTL support with no additional configuration. 18 | - All components are accessible according to [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/) standards. 19 | - Theming support using CSS custom properties. 20 | - Minimal third-party dependency usage. 21 | - Reduced motion support. 22 | - Easy setup. Just install the library, add some base styles, and you're ready to go. 23 | - Minimal CSS overhead. Styles are included and scoped alongside their respective components, only bundling the CSS you need. 24 | 25 | ### Undocumented Components 26 | 27 | The documentation site (https://fluent-svelte.vercel.app/) is still not entirely finished. Many components exported in the library are not yet documented. That progress can be tracked [here](https://github.com/Tropix126/fluent-svelte/issues/13). For now, i've setup a testing page with every component included in the library that is viewable [here](https://fluent-svelte.vercel.app/test). It's source can be viewed [here](https://github.com/Tropix126/fluent-svelte/blob/main/src/routes/test/index.svelte). 28 | 29 | Please keep in mind that any undocumented component is _considered to be in the 0.x phase of development_. This means that they could potentially recieve breaking API changes or be heavily updated before being finalized. 30 | 31 | ### Changelog 32 | 33 | See [CHANGELOG.md](https://github.com/Tropix126/fluent-svelte/blob/main/CHANGELOG.md). 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluent-svelte", 3 | "version": "1.6.0", 4 | "description": "A faithful implementation of Microsoft's Fluent Design System in Svelte.", 5 | "homepage": "https://github.com/tropix126/fluent-svelte", 6 | "license": "MIT", 7 | "author": { 8 | "name": "tropix126", 9 | "url": "https://github.com/tropix126/" 10 | }, 11 | "keywords": [ 12 | "design", 13 | "design-system", 14 | "design-language", 15 | "microsoft", 16 | "fluent", 17 | "fluentui", 18 | "fluent-design", 19 | "fluent-design-system", 20 | "svelte", 21 | "svelte3", 22 | "sveltejs", 23 | "svelte-component", 24 | "svelte-components", 25 | "ui-components", 26 | "winui", 27 | "windows" 28 | ], 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/tropix126/fluent-svelte.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/tropix126/fluent-svelte/issues" 35 | }, 36 | "scripts": { 37 | "dev": "svelte-kit dev", 38 | "build": "svelte-kit build", 39 | "package": "svelte-kit package", 40 | "preview": "svelte-kit preview", 41 | "check": "svelte-check --tsconfig ./tsconfig.json", 42 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", 43 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. .", 44 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." 45 | }, 46 | "devDependencies": { 47 | "@fec/remark-a11y-emoji": "^3.1.0", 48 | "@fluentui/svg-icons": "^1.1.166", 49 | "@sveltejs/adapter-vercel": "next", 50 | "@sveltejs/kit": "^1.0.0-next.310", 51 | "@sveltejs/svelte-repl": "^0.2.2", 52 | "@types/prismjs": "^1.26.0", 53 | "autoprefixer": "^10.4.4", 54 | "cssnano": "^5.1.7", 55 | "mdsvex": "^0.9.8", 56 | "mdsvexamples": "0.2.3", 57 | "panzoom": "^9.4.2", 58 | "postcss": "^8.4.12", 59 | "postcss-variables-prefixer": "^1.1.1", 60 | "prettier": "^2.6.2", 61 | "prettier-plugin-svelte": "^2.7.0", 62 | "prism-svelte": "^0.4.7", 63 | "prismjs": "^1.27.0", 64 | "rehype-slug": "^5.0.1", 65 | "remark-github": "^11.2.2", 66 | "sass": "^1.50.0", 67 | "svelte": "^3.47.0", 68 | "svelte-check": "^2.6.0", 69 | "svelte-codesandbox": "^1.0.0", 70 | "svelte-preprocess": "^4.10.5", 71 | "svelte2tsx": "^0.4.14", 72 | "tslib": "^2.3.1", 73 | "typescript": "^4.6.3", 74 | "vite-node": "^0.1.27", 75 | "vite-plugin-sveld": "^1.0.3" 76 | }, 77 | "dependencies": { 78 | "focus-trap": "^6.7.3", 79 | "tabbable": "^5.2.1" 80 | }, 81 | "type": "module" 82 | } 83 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %svelte.head% 8 | 9 | 10 |
%svelte.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/lib/AutoSuggestBox/AutoSuggestBox.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .auto-suggest-box-flyout { 4 | z-index: 100; 5 | overflow: auto; 6 | position: absolute; 7 | inset-inline-start: -1px; 8 | inset-block-start: calc(100% + 1px); 9 | inline-size: calc(100% + 2px); 10 | margin: 0; 11 | padding: 0; 12 | padding-block: 2px; 13 | box-sizing: border-box; 14 | color: var(--text-primary); 15 | border-radius: var(--overlay-corner-radius); 16 | border-end-start-radius: 0; 17 | border-end-end-radius: 0; 18 | border: 1px solid var(--surface-stroke-flyout); 19 | background-color: var(--solid-background-quarternary); 20 | background-clip: padding-box; 21 | box-shadow: var(--flyout-shadow); 22 | max-block-size: 472px; 23 | } 24 | 25 | .auto-suggest-item-wrapper { 26 | display: block; 27 | } 28 | 29 | :global { 30 | .auto-suggest-box.open { 31 | background-color: var(--control-fill-input-active) !important; 32 | .text-box-underline::after { 33 | content: ""; 34 | border-bottom: 2px solid var(--fds-accent-default); 35 | } 36 | input::placeholder { 37 | color: var(--text-tertiary); 38 | } 39 | .text-box-underline { 40 | border-bottom-left-radius: 0; 41 | border-bottom-right-radius: 0; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/Button/Button.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .button { 4 | @include flex($inline: true, $align: center, $justify: center); 5 | @include typography-body; 6 | 7 | position: relative; 8 | box-sizing: border-box; 9 | padding-block: 4px 6px; 10 | padding-inline: 11px; 11 | text-decoration: none; 12 | border: none; 13 | outline: none; 14 | cursor: default; 15 | border-radius: var(--control-corner-radius); 16 | transition: var(--control-faster-duration) ease background; 17 | 18 | &:focus-visible { 19 | box-shadow: var(--focus-stroke); 20 | } 21 | 22 | &.style- { 23 | &standard { 24 | border: 1px solid; 25 | border-color: var(--control-border-default); 26 | background-color: var(--control-fill-default); 27 | color: var(--text-primary); 28 | background-clip: padding-box; 29 | 30 | &:hover { 31 | background-color: var(--control-fill-secondary); 32 | } 33 | 34 | &:active { 35 | border-color: var(--control-stroke-default); 36 | background-color: var(--control-fill-tertiary); 37 | color: var(--text-secondary); 38 | } 39 | 40 | &.disabled { 41 | border-color: var(--control-stroke-default); 42 | background-color: var(--control-fill-disabled); 43 | color: var(--text-disabled); 44 | } 45 | } 46 | 47 | &accent { 48 | border: 1px solid var(--control-stroke-on-accent-default); 49 | border-bottom-color: var(--control-stroke-on-accent-secondary); 50 | background-color: var(--accent-default); 51 | color: var(--text-on-accent-primary); 52 | transition: var(--control-faster-duration) ease border-color; 53 | 54 | &:hover { 55 | background-color: var(--accent-secondary); 56 | } 57 | 58 | &:active { 59 | border-color: transparent; 60 | background-color: var(--accent-tertiary); 61 | color: var(--text-on-accent-secondary); 62 | } 63 | 64 | &.disabled { 65 | border-color: transparent; 66 | background-color: var(--accent-disabled); 67 | color: var(--text-on-accent-disabled); 68 | } 69 | } 70 | 71 | &hyperlink { 72 | background-color: var(--subtle-fill-transparent); 73 | color: var(--accent-text-primary); 74 | cursor: pointer; 75 | 76 | &:hover { 77 | background-color: var(--subtle-fill-secondary); 78 | } 79 | 80 | &:active { 81 | background-color: var(--subtle-fill-tertiary); 82 | color: var(--accent-text-tertiary); 83 | } 84 | 85 | &.disabled { 86 | color: var(--accent-text-disabled); 87 | } 88 | } 89 | } 90 | 91 | &.disabled { 92 | pointer-events: none; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/lib/Button/Button.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 33 | 43 | 44 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /src/lib/CalendarDatePicker/CalendarDatePicker.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .calendar-date-picker- { 4 | &label { 5 | padding-inline-end: 2px; 6 | &.placeholder:not(.disabled) { 7 | color: var(--text-secondary); 8 | } 9 | } 10 | &icon { 11 | @include icon($size: 12px); 12 | margin-inline-start: 8px; 13 | color: currentColor; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/CalendarView/CalendarView.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .calendar-view { 4 | @include flex($inline: true, $direction: column); 5 | text-align: start; 6 | inline-size: 300px; 7 | block-size: 347px; 8 | position: relative; 9 | user-select: none; 10 | color: var(--text-primary); 11 | background-clip: padding-box; 12 | background-color: var(--fds-solid-background-quarternary); 13 | border-radius: var(--control-corner-radius); 14 | border: 1px solid var(--surface-stroke-flyout); 15 | font-family: var(--font-family-text); 16 | &.floating { 17 | border-radius: var(--overlay-corner-radius); 18 | box-shadow: var(--flyout-shadow); 19 | } 20 | &-header, 21 | &-pagination-controls { 22 | @include flex($align: center); 23 | } 24 | &-pagination-controls button { 25 | padding: 0; 26 | inline-size: 30px; 27 | margin-inline-start: 4px; 28 | } 29 | &-header { 30 | box-sizing: border-box; 31 | border-block-end: 1px solid var(--card-stroke-default); 32 | inline-size: 100%; 33 | padding: 7px; 34 | button { 35 | @include flex($align: center, $justify: center); 36 | border: none; 37 | outline: none; 38 | padding: 0; 39 | min-block-size: 32px; 40 | color: var(--text-primary); 41 | background-color: var(--subtle-fill-transparent); 42 | border-radius: var(--control-corner-radius); 43 | line-height: 20px; 44 | font: { 45 | size: 14px; 46 | weight: 600; 47 | family: var(--font-family-text); 48 | } 49 | &:focus-visible { 50 | box-shadow: var(--focus-stroke); 51 | } 52 | &:hover { 53 | background-color: var(--subtle-fill-secondary); 54 | } 55 | &:active { 56 | background-color: var(--subtle-fill-tertiary); 57 | color: var(--text-secondary); 58 | } 59 | &:disabled { 60 | background-color: var(--sutble-fill-disabled); 61 | color: var(--text-disabled); 62 | svg { 63 | color: var(--control-strong-fill-disabled); 64 | } 65 | } 66 | svg { 67 | @include icon($size: 16px); 68 | color: var(--control-strong-fill-default); 69 | } 70 | } 71 | &-text { 72 | flex: 1 1 auto; 73 | button { 74 | inline-size: 100%; 75 | padding-inline: 9px; 76 | justify-content: flex-start; 77 | flex: 1 1 auto; 78 | } 79 | } 80 | } 81 | &-table { 82 | inset: 0; 83 | display: block; 84 | overflow: hidden; 85 | position: absolute; 86 | box-sizing: border-box; 87 | inline-size: calc(100% - 6px); 88 | block-size: calc(100% - 6px); 89 | margin: 3px; 90 | font-size: 14px; 91 | &-wrapper { 92 | inline-size: 298px; 93 | block-size: 298px; 94 | position: relative; 95 | contain: layout; 96 | overflow: hidden; 97 | background-color: var(--layer-on-acrylic-background-default); 98 | } 99 | &.view- { 100 | &months, 101 | &years { 102 | margin: 11px; 103 | inline-size: calc(100% - 22px); 104 | block-size: calc(100% - 22px); 105 | tr { 106 | grid-template-columns: repeat(4, 1fr); 107 | grid-gap: calc(52px / 3); 108 | margin-block-end: calc(52px / 3); 109 | } 110 | } 111 | } 112 | td, 113 | th { 114 | padding: 0; 115 | } 116 | th { 117 | @include flex($align: center, $justify: center); 118 | block-size: 40px; 119 | text-align: center; 120 | font: { 121 | size: 13px; 122 | weight: 600; 123 | } 124 | } 125 | thead, 126 | tbody { 127 | inline-size: 100%; 128 | display: flex; 129 | flex-direction: column; 130 | } 131 | thead tr, 132 | tbody { 133 | background-color: var(--fds-solid-background-quarternary); 134 | box-shadow: inset 0 0 0 100vmax var(--fds-layer-on-acrylic-background-default); 135 | } 136 | thead { 137 | position: relative; 138 | z-index: 1; 139 | } 140 | tbody { 141 | position: absolute; 142 | inset-inline-start: 0; 143 | inset-block-end: 0; 144 | tr:last-child { 145 | margin-block-end: 0; 146 | } 147 | } 148 | tr { 149 | display: grid; 150 | inline-size: 100%; 151 | grid-template-columns: repeat(7, 1fr); 152 | grid-gap: 2px; 153 | margin-block-end: 2px; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/lib/CalendarView/CalendarViewItem.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .calendar-view-item { 4 | @include flex($inline: true, $justify: center, $align: center); 5 | position: relative; 6 | user-select: none; 7 | text-align: center; 8 | box-sizing: border-box; 9 | padding: 0; 10 | border: 1px solid transparent; 11 | outline: none; 12 | background-color: var(--subtle-fill-transparent); 13 | color: var(--text-primary); 14 | border-radius: 50%; 15 | line-height: 20px; 16 | font: { 17 | family: var(--font-family-text); 18 | size: 14px; 19 | weight: 400; 20 | } 21 | &:focus-visible { 22 | box-shadow: var(--focus-stroke); 23 | } 24 | &:hover { 25 | background-color: var(--subtle-fill-secondary); 26 | } 27 | &:active { 28 | background-color: var(--subtle-fill-tertiary); 29 | color: var(--text-secondary); 30 | } 31 | &.out-of-range { 32 | color: var(--text-secondary); 33 | &:active { 34 | color: var(--text-tertiary); 35 | } 36 | } 37 | &.disabled { 38 | background-color: var(--subtle-fill-disabled); 39 | color: var(--text-disabled); 40 | &.blackout::after { 41 | content: none; 42 | } 43 | } 44 | &.blackout { 45 | pointer-events: none; 46 | &::after { 47 | content: ""; 48 | transform-origin: center; 49 | inline-size: 26px; 50 | block-size: 1px; 51 | position: absolute; 52 | transform: matrix(-0.71, -0.71, -0.71, 0.71, 0, 0); 53 | border-block-start: 1px solid var(--control-strong-stroke-default); 54 | } 55 | } 56 | &.type- { 57 | &day { 58 | inline-size: 40px; 59 | block-size: 40px; 60 | small { 61 | inset-block-start: 2px; 62 | } 63 | } 64 | &month-year { 65 | inline-size: 56px; 66 | block-size: 56px; 67 | small { 68 | inset-block-start: 9.58px; // figma toolkit is weird idk 69 | } 70 | } 71 | } 72 | &.selected { 73 | color: var(--accent-text-primary); 74 | border: 1px solid var(--accent-default); 75 | &:hover { 76 | background-color: var(--subtle-fill-secondary); 77 | border-color: var(--accent-secondary); 78 | } 79 | &:active { 80 | background-color: var(--subtle-fill-tertiary); 81 | border-color: var(--accent-tertiary); 82 | } 83 | &.disabled { 84 | color: var(--accent-text-disabled); 85 | background-color: var(--subtle-fill-disabled); 86 | border-color: var(--accent-disabled); 87 | } 88 | &.current { 89 | box-shadow: inset 0 0 0 1px var(--text-on-accent-primary); 90 | &:focus-visible { 91 | box-shadow: inset 0 0 0 1px var(--text-on-accent-primary), var(--focus-stroke); 92 | } 93 | } 94 | &.blackout::after { 95 | border-block-start-color: var(--accent-tertiary); 96 | } 97 | } 98 | &.current { 99 | color: var(--text-on-accent-primary); 100 | background-color: var(--accent-default); 101 | &:hover { 102 | background-color: var(--accent-secondary); 103 | } 104 | &:active { 105 | background-color: var(--accent-tertiary); 106 | color: var(--text-on-accent-secondary); 107 | } 108 | &.disabled { 109 | background-color: var(--accent-disabled); 110 | } 111 | &.blackout::after { 112 | border-block-start-color: var(--text-on-accent-primary); 113 | } 114 | } 115 | small { 116 | pointer-events: none; 117 | position: absolute; 118 | inline-size: 100%; 119 | padding-inline: 1px; 120 | letter-spacing: 0.04em; 121 | text-align: center; 122 | color: inherit; 123 | line-height: 12px; 124 | font: { 125 | family: var(--font-family-small); 126 | weight: 400; 127 | size: 8px; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/lib/CalendarView/CalendarViewItem.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/lib/Checkbox/Checkbox.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .checkbox { 4 | @include typography-body; 5 | 6 | margin: 0; 7 | border: 1px solid var(--control-strong-stroke-default); 8 | border-radius: var(--control-corner-radius); 9 | outline: none; 10 | background-clip: padding-box; 11 | background-color: var(--control-alt-fill-secondary); 12 | appearance: none; 13 | inline-size: 20px; 14 | block-size: 20px; 15 | 16 | &:focus-visible { 17 | box-shadow: var(--focus-stroke); 18 | } 19 | 20 | &:hover { 21 | background-color: var(--control-alt-fill-tertiary); 22 | } 23 | 24 | &:active { 25 | border-color: var(--control-strong-stroke-disabled); 26 | background-color: var(--control-alt-fill-quarternary); 27 | + .checkbox-glyph { 28 | color: var(--text-on-accent-secondary); 29 | } 30 | } 31 | 32 | &:disabled { 33 | border-color: var(--control-strong-stroke-disabled); 34 | background-color: var(--control-alt-fill-disabled); 35 | pointer-events: none; 36 | } 37 | 38 | &:checked, 39 | &:indeterminate { 40 | border: none; 41 | background-color: var(--accent-default); 42 | 43 | &:hover { 44 | background-color: var(--accent-secondary); 45 | } 46 | 47 | &:active { 48 | background-color: var(--accent-tertiary); 49 | } 50 | 51 | &:disabled { 52 | border-color: var(--control-strong-stroke-disabled); 53 | background-color: var(--accent-disabled); 54 | + .checkbox-glyph { 55 | color: var(--text-on-accent-disabled); 56 | } 57 | } 58 | 59 | + .checkbox-glyph .path-checkmark { 60 | transition: var(--control-normal-duration) cubic-bezier(0.55, 0, 0, 1) stroke-dashoffset; 61 | stroke-dashoffset: 0; 62 | } 63 | } 64 | 65 | &-container { 66 | @include flex($inline: true, $align: center); 67 | @include typography-body; 68 | 69 | color: var(--text-primary); 70 | user-select: none; 71 | min-block-size: 32px; 72 | 73 | > span { 74 | padding-inline-start: 8px; 75 | } 76 | 77 | &.disabled > span { 78 | color: var(--text-disabled); 79 | } 80 | } 81 | 82 | &-inner { 83 | @include flex($align: center, $justify: center); 84 | position: relative; 85 | } 86 | 87 | &-glyph { 88 | position: absolute; 89 | color: inherit; 90 | color: var(--text-on-accent-primary); 91 | inline-size: 12px; 92 | block-size: 12px; 93 | 94 | path { 95 | transform-origin: center; 96 | } 97 | 98 | .path- { 99 | &checkmark { 100 | transform: scale(1.2); 101 | stroke: currentColor; 102 | stroke: { 103 | width: 2; 104 | linecap: round; 105 | linejoin: round; 106 | dasharray: 20.5; 107 | dashoffset: 20.5; 108 | } 109 | } 110 | 111 | &indeterminate { 112 | transform: scale(calc(2 / 3)) translateX(80px) translateY(240px); 113 | fill: currentColor; 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/lib/Checkbox/Checkbox.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 | 38 | 39 | 40 | 78 | 79 | 82 | -------------------------------------------------------------------------------- /src/lib/ComboBox/ComboBox.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | @keyframes menu-in { 4 | 0% { 5 | clip-path: var(--grow-clip-path); 6 | } 7 | 100% { 8 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 9 | } 10 | } 11 | 12 | @keyframes shadow-in { 13 | 0% { 14 | box-shadow: none; 15 | } 16 | 100% { 17 | box-shadow: var(--flyout-shadow); 18 | } 19 | } 20 | 21 | .combo-box { 22 | position: relative; 23 | display: inline-flex; 24 | user-select: none; 25 | 26 | :global { 27 | .button, 28 | .text-box { 29 | flex: 1 1 auto; 30 | } 31 | 32 | .text-box { 33 | border-color: var(--control-border-default); 34 | &-underline::after { 35 | border-color: transparent; 36 | } 37 | &-container { 38 | cursor: default; 39 | &:focus-visible { 40 | cursor: text; 41 | } 42 | } 43 | } 44 | } 45 | 46 | &.editable { 47 | :global { 48 | .combo-box-textbox:not(:focus-within) { 49 | cursor: default; 50 | border-color: var(--control-border-default); 51 | .text-box-underline::after { 52 | content: none; 53 | } 54 | } 55 | &.combo-box-textbox.disabled { 56 | border-color: var(--control-stroke-default); 57 | } 58 | } 59 | &.open { 60 | :global { 61 | .combo-box-textbox { 62 | cursor: text; 63 | background-color: var(--control-fill-input-active); 64 | .text-box-underline::after { 65 | content: ""; 66 | border-bottom: 2px solid var(--fds-accent-default); 67 | } 68 | input::placeholder { 69 | color: var(--text-tertiary); 70 | } 71 | } 72 | .text-box-underline { 73 | border-end-start-radius: 0; 74 | border-end-end-radius: 0; 75 | } 76 | } 77 | } 78 | .combo-box-dropdown { 79 | margin: 0; 80 | inset-inline-start: 0; 81 | inset-block-start: 100%; 82 | inline-size: 100%; 83 | border-radius: var(--overlay-corner-radius); 84 | border-start-start-radius: 0; 85 | border-start-end-radius: 0; 86 | } 87 | .combo-box-icon { 88 | margin: 0; 89 | } 90 | } 91 | 92 | &-label { 93 | flex: 1 1 auto; 94 | text-align: start; 95 | min-block-size: 20px; 96 | 97 | &.placeholder { 98 | color: var(--text-secondary); 99 | } 100 | } 101 | 102 | &.disabled .placeholder { 103 | color: var(--text-disabled); 104 | } 105 | 106 | &-icon { 107 | margin-inline-start: 8px; 108 | inline-size: 12px; 109 | block-size: 12px; 110 | } 111 | 112 | &-dropdown { 113 | z-index: 100; 114 | position: absolute; 115 | box-sizing: border-box; 116 | margin: 0; 117 | margin-block-start: -6px; 118 | margin-inline-start: -5px; 119 | padding: 1px; 120 | border: 1px solid var(--surface-stroke-flyout); 121 | border-radius: var(--overlay-corner-radius); 122 | background-color: var(--solid-background-quarternary); 123 | background-clip: padding-box; 124 | box-shadow: var(--flyout-shadow); 125 | animation: menu-in var(--control-normal-duration) var(--control-fast-out-slow-in-easing), 126 | shadow-in var(--control-normal-duration) var(--control-fast-out-slow-in-easing) 127 | var(--control-normal-duration); 128 | overflow: auto; 129 | inline-size: calc(100% + 8px); 130 | max-block-size: 504px; 131 | inset-block-start: var(--menu-offset, 0); 132 | inset-inline-start: 0; 133 | 134 | @supports (overflow: overlay) { 135 | overflow: overlay; 136 | } 137 | 138 | &.direction- { 139 | &top { 140 | --grow-clip-path: polygon(0 0, 100% 0, 100% 25%, 0 25%); 141 | } 142 | 143 | ¢er { 144 | --grow-clip-path: polygon(0 25%, 100% 24%, 100% 75%, 0 75%); 145 | } 146 | 147 | &bottom { 148 | --grow-clip-path: polygon(0 75%, 100% 75%, 100% 100%, 0 100%); 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/lib/ComboBox/ComboBoxItem.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .combo-box-item { 4 | @include flex($align: center); 5 | @include typography-body; 6 | 7 | position: relative; 8 | box-sizing: border-box; 9 | flex: 0 0 auto; 10 | margin: 4px; 11 | padding: 0 11px; 12 | border-radius: var(--control-corner-radius); 13 | outline: none; 14 | background-color: var(--subtle-fill-transparent); 15 | color: var(--text-primary); 16 | text-decoration: none; 17 | cursor: default; 18 | user-select: none; 19 | block-size: 32px; 20 | 21 | &::before { 22 | content: ""; 23 | position: absolute; 24 | border-radius: 3px; 25 | background-color: var(--accent-default); 26 | transition: transform var(--control-fast-duration) var(--control-fast-out-slow-in-easing); 27 | opacity: 0; 28 | inset-inline-start: 0; 29 | inline-size: 3px; 30 | block-size: 0; 31 | } 32 | 33 | &:focus-visible { 34 | box-shadow: var(--focus-stroke); 35 | } 36 | 37 | &:hover, 38 | &.selected { 39 | background-color: var(--subtle-fill-secondary); 40 | } 41 | 42 | &:active { 43 | background-color: var(--subtle-fill-tertiary); 44 | color: var(--text-secondary); 45 | 46 | &::before { 47 | transform: scaleY(0.625); 48 | } 49 | } 50 | 51 | &.disabled { 52 | background-color: var(--subtle-fill-transparent); 53 | color: var(--text-disabled); 54 | pointer-events: none; 55 | &.selected { 56 | background-color: var(--subtle-fill-secondary); 57 | &::before { 58 | background-color: var(--accent-disabled); 59 | } 60 | } 61 | } 62 | 63 | &.selected::before { 64 | opacity: 1; 65 | block-size: 16px; 66 | } 67 | 68 | > span { 69 | flex: 1 1 auto; 70 | text-overflow: ellipsis; 71 | white-space: nowrap; 72 | overflow: hidden; 73 | max-inline-size: 100%; 74 | } 75 | 76 | > :global(svg) { 77 | @include icon($size: 16px); 78 | margin-inline-end: 16px; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/lib/ComboBox/ComboBoxItem.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
  • 22 | 23 | 24 | 25 | 26 |
  • 27 | 28 | 31 | -------------------------------------------------------------------------------- /src/lib/ContentDialog/ContentDialog.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .content-dialog { 4 | position: fixed; 5 | box-sizing: border-box; 6 | animation: dialog-inner var(--control-fast-duration) var(--control-fast-out-slow-in-easing); 7 | max-inline-size: calc(100% - 24px); 8 | border-radius: var(--overlay-corner-radius); 9 | background-color: var(--solid-background-base); 10 | background-clip: padding-box; 11 | box-shadow: var(--dialog-shadow); 12 | border: 1px solid var(--surface-stroke-default); 13 | overflow: hidden; 14 | &.size- { 15 | &min { 16 | inline-size: 320px; 17 | } 18 | &standard { 19 | inline-size: 448px; 20 | } 21 | &max { 22 | inline-size: 540px; 23 | } 24 | } 25 | 26 | &-smoke { 27 | @include flex($direction: column, $align: center, $justify: center); 28 | position: fixed; 29 | inset-inline-start: 0; 30 | inset-block-start: 0; 31 | z-index: 101; 32 | inline-size: 100%; 33 | block-size: 100%; 34 | &.darken { 35 | background-color: var(--smoke-background-default); 36 | } 37 | } 38 | 39 | :global(.content-dialog-title) { 40 | display: block; 41 | margin-bottom: 12px; 42 | color: var(--text-primary); 43 | } 44 | 45 | &-body, 46 | &-footer { 47 | padding: 24px; 48 | } 49 | 50 | &-body { 51 | @include typography-body; 52 | background-color: var(--layer-background-default); 53 | color: var(--text-primary); 54 | } 55 | 56 | &-footer { 57 | display: grid; 58 | grid-auto-rows: 1fr; 59 | grid-auto-flow: column; 60 | grid-gap: 8px; 61 | border-block-start: 1px solid var(--card-stroke-default); 62 | white-space: nowrap; 63 | > :global(.button:only-child) { 64 | inline-size: 50%; 65 | justify-self: end; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/lib/ContentDialog/ContentDialog.svelte: -------------------------------------------------------------------------------- 1 | 74 | 75 | 76 | 77 | {#if open} 78 |
    dispatch("backdropclick", e)} 82 | on:mousedown|self={e => dispatch("backdropmousedown", e)} 83 | transition:fade|local={{ duration: getCSSDuration("--fds-control-faster-duration") }} 84 | use:mountDialog 85 | use:_focusTrap 86 | bind:this={backdropElement} 87 | > 88 | 117 | 118 |
    119 | {/if} 120 | 121 | 124 | -------------------------------------------------------------------------------- /src/lib/ContextMenu/ContextMenu.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .context-menu- { 4 | &wrapper { 5 | display: contents; 6 | } 7 | &anchor { 8 | position: fixed; 9 | z-index: 10000; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/ContextMenu/ContextMenu.svelte: -------------------------------------------------------------------------------- 1 | 74 | 75 | 76 | 77 |
    83 | 84 | {#if open} 85 |
    e.preventDefault()} 90 | bind:this={anchorElement} 91 | on:outermousedown={() => (open = false)} 92 | class="context-menu-anchor" 93 | style="top: {menuPosition.y}px; left: {menuPosition.x}px;" 94 | > 95 | 96 | 97 | 98 |
    99 | {/if} 100 |
    101 | 102 | 105 | -------------------------------------------------------------------------------- /src/lib/Expander/Expander.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .expander { 4 | @include flex($direction: column); 5 | color: var(--text-primary); 6 | border-radius: var(--control-corner-radius); 7 | inline-size: 100%; 8 | user-select: none; 9 | &.direction- { 10 | &down { 11 | .expander-content { 12 | border-block-start: none; 13 | border-radius: var(--control-corner-radius); 14 | border-start-start-radius: 0; 15 | border-start-end-radius: 0; 16 | transform: translateY(-100%); 17 | } 18 | &.expanded .expander-header { 19 | border-radius: var(--control-corner-radius); 20 | border-end-start-radius: 0; 21 | border-end-end-radius: 0; 22 | } 23 | } 24 | &up { 25 | .expander-content { 26 | border-bottom: none; 27 | border-radius: var(--control-corner-radius); 28 | border-end-start-radius: 0; 29 | border-end-end-radius: 0; 30 | transform: translateY(100%); 31 | &-anchor { 32 | order: -1; 33 | } 34 | } 35 | &.expanded .expander-header { 36 | border-radius: var(--control-corner-radius); 37 | border-start-start-radius: 0; 38 | border-start-end-radius: 0; 39 | } 40 | } 41 | } 42 | &.expanded { 43 | .expander { 44 | &-content { 45 | transform: none; 46 | transition: var(--control-slow-duration) var(--control-fast-out-slow-in-easing) 47 | transform; 48 | &-anchor { 49 | max-block-size: 6.02e23vmax; 50 | transition: none; 51 | } 52 | } 53 | &-chevron svg { 54 | transform: rotate(180deg); 55 | } 56 | } 57 | } 58 | > h3 { 59 | display: contents; 60 | } 61 | &-icon { 62 | flex: 0 0 auto; 63 | color: var(--text-primary); 64 | inline-size: 16px; 65 | block-size: 16px; 66 | margin-inline-end: 16px; 67 | > :global(svg) { 68 | @include icon($size: 16px); 69 | } 70 | } 71 | &-header { 72 | @include flex($align: center); 73 | @include typography-body; 74 | text-align: start; 75 | outline: none; 76 | box-sizing: border-box; 77 | padding-inline-start: 16px; 78 | padding: 8px; 79 | background-clip: padding-box; 80 | background-color: var(--card-background-default); 81 | border: 1px solid var(--card-stroke-default); 82 | border-radius: var(--control-corner-radius); 83 | &-title { 84 | flex: 1 1 auto; 85 | } 86 | &:focus-visible { 87 | box-shadow: var(--focus-stroke); 88 | } 89 | &:hover .expander-chevron { 90 | background-color: var(--subtle-fill-secondary); 91 | } 92 | &:active .expander-chevron { 93 | color: var(--text-secondary); 94 | background-color: var(--subtle-fill-tertiary); 95 | } 96 | } 97 | &-chevron { 98 | @include flex($align: center, $justify: center); 99 | flex: 0 0 auto; 100 | inline-size: 32px; 101 | block-size: 32px; 102 | margin-inline-start: 20px; 103 | border: none; 104 | outline: none; 105 | appearance: none; 106 | color: var(--text-primary); 107 | border-radius: var(--control-corner-radius); 108 | background-color: var(--subtle-fill-transparent); 109 | &:focus-visible { 110 | box-shadow: var(--focus-stroke); 111 | } 112 | svg { 113 | inline-size: 12px; 114 | block-size: 12px; 115 | fill: currentColor; 116 | transition: calc(var(--control-faster-duration) * 1.2) linear transform 117 | var(--control-faster-duration); 118 | } 119 | } 120 | &-content { 121 | @include typography-body; 122 | background-clip: padding-box; 123 | background-color: var(--card-background-secondary); 124 | border: 1px solid var(--card-stroke-default); 125 | padding: 16px; 126 | transition: var(--control-fast-duration) cubic-bezier(1, 1, 0, 1) transform; 127 | &-anchor { 128 | max-height: 0; 129 | position: relative; 130 | overflow: hidden; 131 | transition: 0ms linear var(--control-slow-duration) max-height; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/lib/Expander/Expander.svelte: -------------------------------------------------------------------------------- 1 | 49 | 50 | 63 |
    71 | 72 |
    (expanded = !expanded)} 82 | > 83 | {#if $$slots.icon} 84 |
    85 | 86 |
    87 | {/if} 88 | 89 | 90 | 91 | 112 |
    113 |
    114 |
    115 |
    116 | 117 |
    118 |
    119 |
    120 | 121 | 124 | -------------------------------------------------------------------------------- /src/lib/Flyout/FlyoutSurface.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .flyout { 4 | @include typography-body; 5 | padding: 16px; 6 | min-inline-size: 320px; 7 | box-sizing: border-box; 8 | color: var(--text-primary); 9 | border-radius: var(--overlay-corner-radius); 10 | border: 1px solid var(--surface-stroke-flyout); 11 | background-color: var(--solid-background-quarternary); 12 | background-clip: padding-box; 13 | box-shadow: var(--flyout-shadow); 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/Flyout/FlyoutSurface.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
    16 | 17 |
    18 | 19 | 22 | -------------------------------------------------------------------------------- /src/lib/Flyout/FlyoutWrapper.scss: -------------------------------------------------------------------------------- 1 | @keyframes flyout-in { 2 | 0% { 3 | opacity: 0; 4 | transform: var(--flyout-transform) var(--flyout-transition-offset, translateY(12px)); 5 | } 6 | 100% { 7 | opacity: 1; 8 | transform: var(--flyout-transform); 9 | } 10 | } 11 | 12 | .flyout- { 13 | &wrapper { 14 | display: inline-block; 15 | inline-size: fit-content; 16 | block-size: fit-content; 17 | position: relative; 18 | } 19 | &backdrop { 20 | position: fixed; 21 | top: 0; 22 | left: 0; 23 | inline-size: 100%; 24 | block-size: 100%; 25 | z-index: 9999; 26 | } 27 | &anchor { 28 | position: absolute; 29 | z-index: 10000; 30 | animation: flyout-in var(--control-normal-duration) var(--control-fast-out-slow-in-easing); 31 | transform: var(--flyout-transform); 32 | &.placement- { 33 | &top { 34 | bottom: calc(100% + var(--flyout-offset)); 35 | --flyout-transition-offset: translateY(12px); 36 | } 37 | &bottom { 38 | top: calc(100% + var(--flyout-offset)); 39 | --flyout-transition-offset: translateY(-12px); 40 | } 41 | &left { 42 | right: calc(100% + var(--flyout-offset)); 43 | --flyout-transition-offset: translateX(12px); 44 | } 45 | &right { 46 | left: calc(100% + var(--flyout-offset)); 47 | --flyout-transition-offset: translateX(-12px); 48 | } 49 | &top, 50 | &bottom { 51 | &.alignment- { 52 | &start { 53 | inset-inline-start: 0; 54 | } 55 | &end { 56 | inset-inline-end: 0; 57 | } 58 | ¢er { 59 | inset-inline-start: 50%; 60 | --flyout-transform: translateX(-50%); 61 | } 62 | } 63 | } 64 | &left, 65 | &right { 66 | &.alignment- { 67 | &start { 68 | inset-block-start: 0; 69 | } 70 | &end { 71 | inset-block-end: 0; 72 | } 73 | ¢er { 74 | inset-block-start: 50%; 75 | --flyout-transform: translateY(-50%); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/lib/Flyout/FlyoutWrapper.svelte: -------------------------------------------------------------------------------- 1 | 68 | 69 | 82 | 83 | 84 | 85 |
    (open = !open)} 91 | on:keydown={handleKeyDown} 92 | bind:this={wrapperElement} 93 | > 94 | 95 | {#if open} 96 |
    e.stopPropagation()} 107 | {...$$restProps} 108 | > 109 | 110 | 111 | 112 | 113 | 114 |
    115 |
    e.stopPropagation()} 119 | on:mousedown={closeFlyout} 120 | /> 121 | {/if} 122 |
    123 | 124 | 127 | -------------------------------------------------------------------------------- /src/lib/IconButton/IconButton.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .icon-button { 4 | @include flex($inline: true, $align: center, $justify: center); 5 | outline: none; 6 | border: none; 7 | box-sizing: border-box; 8 | min-inline-size: 30px; 9 | min-block-size: 30px; 10 | padding: 8px; 11 | color: var(--text-primary); 12 | border-radius: var(--control-corner-radius); 13 | background-color: var(--subtle-fill-transparent); 14 | &:focus-visible { 15 | box-shadow: var(--focus-stroke); 16 | } 17 | &:hover { 18 | background-color: var(--subtle-fill-secondary); 19 | } 20 | &:active { 21 | color: var(--text-secondary); 22 | background-color: var(--subtle-fill-tertiary); 23 | } 24 | &:disabled { 25 | background-color: var(--subtle-fill-disabled); 26 | color: var(--text-disabled); 27 | } 28 | :global(svg) { 29 | @include icon($size: 16px); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/IconButton/IconButton.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 34 | 44 | 45 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /src/lib/InfoBadge/InfoBadge.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .info-badge { 4 | @include flex($inline: true, $align: center, $justify: center); 5 | box-sizing: border-box; 6 | user-select: none; 7 | min-inline-size: 16px; 8 | min-block-size: 16px; 9 | border-radius: 16px; 10 | padding: 2px 4px; 11 | color: var(--text-on-accent-primary); 12 | line-height: var(--caption-font-size); 13 | font: { 14 | family: var(--font-family-small); 15 | size: var(--caption-font-size); 16 | } 17 | &.severity- { 18 | &attention { 19 | background-color: var(--system-attention); 20 | } 21 | &success { 22 | background-color: var(--system-success); 23 | } 24 | &caution { 25 | background-color: var(--system-caution); 26 | } 27 | &critical { 28 | background-color: var(--system-critical); 29 | } 30 | &information { 31 | background-color: var(--system-solid-neutral); 32 | } 33 | } 34 | :global(svg) { 35 | inline-size: 8px; 36 | block-size: 8px; 37 | fill: currentColor; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/InfoBar/InfoBar.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .info-bar { 4 | @include flex($align: center); 5 | position: relative; 6 | min-block-size: 48px; 7 | padding-inline-start: 15px; 8 | box-sizing: border-box; 9 | user-select: none; 10 | background-clip: padding-box; 11 | font-family: var(--font-family-text); 12 | border: 1px solid var(--card-stroke-default); 13 | border-radius: var(--control-corner-radius); 14 | &.severity- { 15 | &information { 16 | background-color: var(--card-background-secondary); 17 | } 18 | &success { 19 | background-color: var(--system-background-success); 20 | } 21 | &caution { 22 | background-color: var(--system-background-caution); 23 | } 24 | &critical { 25 | background-color: var(--system-background-critical); 26 | } 27 | &attention { 28 | background-color: var(--system-background-attention); 29 | } 30 | } 31 | &-icon { 32 | align-self: flex-start; 33 | display: flex; 34 | flex: 0 0 auto; 35 | margin: { 36 | block-start: 16px; 37 | } 38 | } 39 | &-content { 40 | @include flex($align: center, $wrap: true); 41 | position: relative; 42 | box-sizing: border-box; 43 | flex: 1 1 auto; 44 | margin: { 45 | inline-start: 13px; 46 | block-start: 7px; 47 | block-end: 7px; 48 | } 49 | &.message-wrapped, 50 | &.action-wrapped { 51 | margin: { 52 | block-start: 13px; 53 | block-end: 15px; 54 | } 55 | } 56 | &.message-wrapped { 57 | h5, 58 | p { 59 | align-self: flex-start; 60 | } 61 | .info-bar-action { 62 | margin-inline-end: 50%; 63 | } 64 | } 65 | &.action-wrapped .info-bar-action { 66 | padding-block-start: 16px; 67 | } 68 | } 69 | h5, 70 | p { 71 | color: var(--text-primary); 72 | margin: 0; 73 | line-height: 20px; 74 | font: { 75 | size: var(--body-font-size); 76 | weight: 400; 77 | } 78 | } 79 | h5 { 80 | font-weight: 600; 81 | margin-inline-end: 12px; 82 | } 83 | p { 84 | flex: 1 1 auto; 85 | margin-inline-end: 15px; 86 | } 87 | &-action { 88 | @include flex($align: center); 89 | align-self: flex-start; 90 | margin-inline-end: 4px; 91 | } 92 | &-close-button { 93 | @include flex($align: center, $justify: center); 94 | align-self: flex-start; 95 | flex: 0 0 auto; 96 | border: none; 97 | outline: none; 98 | appearance: none; 99 | inline-size: 38px; 100 | block-size: 38px; 101 | margin: 4px; 102 | color: var(--text-primary); 103 | border-radius: var(--control-corner-radius); 104 | background-color: var(--subtle-fill-transparent); 105 | transition: var(--control-fast-duration) var(--control-fast-out-slow-in-easing); 106 | &:focus-visible { 107 | box-shadow: var(--focus-stroke); 108 | } 109 | &:hover { 110 | background-color: var(--subtle-fill-secondary); 111 | } 112 | &:active { 113 | color: var(--text-secondary); 114 | background-color: var(--subtle-fill-tertiary); 115 | } 116 | svg { 117 | inline-size: 12px; 118 | block-size: 12px; 119 | fill: currentColor; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/lib/ListItem/ListItem.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .list-item { 4 | @include flex($align: center); 5 | 6 | inline-size: calc(100% - 10px); 7 | position: relative; 8 | box-sizing: border-box; 9 | flex: 0 0 auto; 10 | margin: 3px 5px; 11 | padding-inline: 12px; 12 | border-radius: var(--control-corner-radius); 13 | outline: none; 14 | background-color: var(--subtle-fill-transparent); 15 | color: var(--text-primary); 16 | text-decoration: none; 17 | cursor: default; 18 | user-select: none; 19 | block-size: 34px; 20 | text-decoration: none; 21 | 22 | &::before { 23 | content: ""; 24 | position: absolute; 25 | border-radius: 3px; 26 | background-color: var(--accent-default); 27 | transition: transform var(--control-fast-duration) var(--control-fast-out-slow-in-easing); 28 | opacity: 0; 29 | inset-inline-start: 0; 30 | inline-size: 3px; 31 | block-size: 16px; 32 | transform: scaleY(0); 33 | } 34 | 35 | &.selected::before { 36 | transform: scaleY(1); 37 | opacity: 1; 38 | } 39 | 40 | &:focus-visible { 41 | box-shadow: var(--focus-stroke); 42 | } 43 | 44 | &:hover, 45 | &.selected { 46 | background-color: var(--subtle-fill-secondary); 47 | } 48 | 49 | &:active { 50 | background-color: var(--subtle-fill-tertiary); 51 | color: var(--text-secondary); 52 | 53 | &::before { 54 | transform: scaleY(0.625); 55 | } 56 | } 57 | 58 | &.disabled { 59 | background-color: var(--subtle-fill-transparent); 60 | color: var(--text-disabled); 61 | pointer-events: none; 62 | &.selected { 63 | background-color: var(--subtle-fill-secondary); 64 | &::before { 65 | background-color: var(--accent-disabled); 66 | } 67 | } 68 | } 69 | 70 | > :global(svg) { 71 | @include icon($size: 16px); 72 | margin-inline-end: 16px; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/lib/ListItem/ListItem.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 46 | {#if href && !disabled} 47 | 60 | 61 | 62 | 63 | 64 | 65 | {:else} 66 |
  • 79 | 80 | 81 | 82 | 83 |
  • 84 | {/if} 85 | 86 | 89 | -------------------------------------------------------------------------------- /src/lib/MenuBar/MenuBar.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .menu-bar { 4 | @include flex($align: center); 5 | cursor: default; 6 | user-select: none; 7 | block-size: 40px; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/MenuBar/MenuBar.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /src/lib/MenuBar/MenuBarItem.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .menu-bar-item { 4 | @include typography-body; 5 | @include flex($inline: true, $align: center); 6 | 7 | position: relative; 8 | padding: 5px 11px; 9 | margin: 4px; 10 | cursor: default; 11 | color: var(--text-primary); 12 | background-color: var(--subtle-fill-transparent); 13 | border-radius: var(--control-corner-radius); 14 | &:hover { 15 | background-color: var(--subtle-fill-secondary); 16 | } 17 | &:active, 18 | &[aria-expanded="true"] { 19 | background-color: var(--subtle-fill-tertiary); 20 | &:hover { 21 | background-color: var(--subtle-fill-secondary); 22 | } 23 | } 24 | &:active { 25 | color: var(--text-secondary); 26 | } 27 | &.disabled { 28 | color: var(--text-disabled); 29 | background-color: var(--subtle-fill-disabled) !important; 30 | } 31 | } 32 | 33 | .menu-flyout-anchor { 34 | z-index: 10000; 35 | position: absolute; 36 | inset-block-start: 100%; 37 | inset-inline-start: 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/MenuBar/MenuBarItem.svelte: -------------------------------------------------------------------------------- 1 | 97 | 98 | 99 | 100 | 132 | 133 | 136 | -------------------------------------------------------------------------------- /src/lib/MenuBar/flyoutState.ts: -------------------------------------------------------------------------------- 1 | import type { SvelteComponentTyped } from "svelte"; 2 | 3 | import { writable } from "svelte/store"; 4 | 5 | export const currentMenu = writable(null); 6 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutDivider.scss: -------------------------------------------------------------------------------- 1 | .menu-flyout-divider { 2 | inline-size: 100%; 3 | margin-block: 2px; 4 | block-size: 1px; 5 | border: none; 6 | border-block-start: 1px solid var(--divider-stroke-default); 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutDivider.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutItem.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .menu-flyout-item { 4 | @include flex($align: center); 5 | @include typography-body; 6 | 7 | inline-size: calc(100% - 8px); 8 | position: relative; 9 | box-sizing: border-box; 10 | flex: 0 0 auto; 11 | // overflow: hidden; 12 | margin: 2px 4px; 13 | padding-inline: 12px; 14 | border-radius: var(--control-corner-radius); 15 | outline: none; 16 | background-color: var(--subtle-fill-transparent); 17 | color: var(--text-primary); 18 | text-decoration: none; 19 | cursor: default; 20 | user-select: none; 21 | block-size: 28px; 22 | white-space: nowrap; 23 | text-overflow: ellipsis; 24 | text-decoration: none; 25 | 26 | &::before { 27 | content: ""; 28 | position: absolute; 29 | border-radius: 3px; 30 | background-color: var(--accent-default); 31 | transition: transform var(--control-fast-duration) var(--control-fast-out-slow-in-easing); 32 | opacity: 0; 33 | inset-inline-start: 0; 34 | inline-size: 3px; 35 | block-size: 0; 36 | } 37 | 38 | &:focus-visible { 39 | box-shadow: var(--focus-stroke); 40 | } 41 | 42 | &:hover, 43 | &[aria-expanded="true"], 44 | &.selected { 45 | background-color: var(--subtle-fill-secondary); 46 | } 47 | 48 | &.checked { 49 | :global { 50 | .menu-flyout-item- { 51 | &bullet, 52 | &checkmark { 53 | visibility: visible; 54 | } 55 | } 56 | } 57 | } 58 | 59 | &:active { 60 | background-color: var(--subtle-fill-tertiary); 61 | 62 | &::before { 63 | transform: scaleY(0.625); 64 | } 65 | } 66 | 67 | &.disabled { 68 | background-color: var(--subtle-fill-transparent); 69 | color: var(--text-disabled); 70 | pointer-events: none; 71 | &.selected { 72 | background-color: var(--subtle-fill-secondary); 73 | &::before { 74 | background-color: var(--accent-disabled); 75 | } 76 | } 77 | > :global(.menu-flyout-item-hint) { 78 | color: var(--text-disabled); 79 | } 80 | } 81 | 82 | &.selected::before { 83 | opacity: 1; 84 | block-size: 16px; 85 | } 86 | 87 | &.indented { 88 | padding-inline-start: 40px; 89 | } 90 | 91 | &-checkmark, 92 | &-bullet { 93 | visibility: hidden; 94 | } 95 | 96 | & &-arrow { 97 | box-sizing: content-box; 98 | inline-size: 12px; 99 | block-size: 12px; 100 | margin-inline-end: 0; 101 | margin-inline-start: auto; 102 | padding-inline-start: 24px; 103 | } 104 | 105 | &-checkmark { 106 | @include flex($align: center, $justify: center); 107 | inline-size: 12px; 108 | block-size: 12px; 109 | margin-inline-start: 2px; 110 | margin-inline-end: 14px; 111 | } 112 | 113 | &-bullet { 114 | inline-size: 4px; 115 | block-size: 4px; 116 | border-radius: 4px; 117 | margin-inline-start: 6px; 118 | margin-inline-end: 18px; 119 | background-color: currentColor; 120 | } 121 | 122 | &-input-label { 123 | display: contents; 124 | } 125 | 126 | > :global(svg) { 127 | @include icon($size: 16px); 128 | margin-inline-end: 12px; 129 | } 130 | 131 | > :global(.menu-flyout-item-hint) { 132 | flex: 1 1 auto; 133 | text-align: end; 134 | padding-left: 24px; 135 | overflow: hidden; 136 | text-overflow: ellipsis; 137 | color: var(--text-secondary); 138 | } 139 | } 140 | 141 | .menu-flyout-submenu-anchor { 142 | --fds-menu-flyout-transition-offset: -50%; 143 | z-index: 10000; 144 | position: absolute; 145 | inset-block-start: 0; 146 | inset-inline-start: 100%; 147 | } 148 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutSurface.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | @keyframes menu-open { 4 | from { 5 | transform: translateY(var(--menu-flyout-transition-offset, -50%)); 6 | } 7 | to { 8 | transform: none; 9 | } 10 | } 11 | 12 | @keyframes menu-shadow { 13 | from { 14 | box-shadow: none; 15 | } 16 | to { 17 | box-shadow: var(--flyout-shadow); 18 | } 19 | } 20 | 21 | .menu-flyout { 22 | @include typography-body; 23 | @include flex($direction: column); 24 | animation: menu-open var(--control-normal-duration) var(--control-fast-out-slow-in-easing), 25 | menu-shadow var(--control-fast-duration) var(--control-fast-out-slow-in-easing) 26 | var(--control-normal-duration) forwards; 27 | min-inline-size: 120px; 28 | max-inline-size: 100%; 29 | max-block-size: 100vh; 30 | margin: 0; 31 | padding: 0; 32 | padding-block: 2px; 33 | box-sizing: border-box; 34 | color: var(--text-primary); 35 | border-radius: var(--overlay-corner-radius); 36 | border: 1px solid var(--surface-stroke-flyout); 37 | background-color: var(--solid-background-quarternary); 38 | background-clip: padding-box; 39 | &-surface-container { 40 | overflow: hidden; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutSurface.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutWrapper.scss: -------------------------------------------------------------------------------- 1 | .menu-flyout- { 2 | &wrapper { 3 | display: inline-block; 4 | height: auto; 5 | position: relative; 6 | } 7 | &backdrop { 8 | position: fixed; 9 | top: 0; 10 | left: 0; 11 | width: 100%; 12 | height: 100%; 13 | z-index: 9999; 14 | } 15 | &anchor { 16 | position: absolute; 17 | z-index: 10000; 18 | &.placement- { 19 | &top { 20 | --fds-menu-flyout-transition-offset: 50%; 21 | bottom: calc(100% + var(--menu-flyout-offset)); 22 | } 23 | &bottom { 24 | top: calc(100% + var(--menu-flyout-offset)); 25 | } 26 | &left { 27 | right: calc(100% + var(--menu-flyout-offset)); 28 | } 29 | &right { 30 | left: calc(100% + var(--menu-flyout-offset)); 31 | } 32 | &top, 33 | &bottom { 34 | &.alignment- { 35 | &start { 36 | inset-inline-start: 0; 37 | } 38 | &end { 39 | inset-inline-end: 0; 40 | } 41 | ¢er { 42 | inset-inline-start: 50%; 43 | transform: translateX(-50%); 44 | } 45 | } 46 | } 47 | &left, 48 | &right { 49 | &.alignment- { 50 | &start { 51 | inset-block-start: 0; 52 | } 53 | &end { 54 | inset-block-end: 0; 55 | } 56 | ¢er { 57 | inset-block-start: 50%; 58 | transform: translateY(-50%); 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lib/MenuFlyout/MenuFlyoutWrapper.svelte: -------------------------------------------------------------------------------- 1 | 77 | 78 | 79 | 80 |