@kth/style 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +28 -22
  2. package/dist/cjs/components/MenuPanel.d.ts +13 -0
  3. package/dist/cjs/index.d.ts +1 -3
  4. package/dist/cjs/index.js +54 -127
  5. package/dist/cjs/react.d.ts +1 -5
  6. package/dist/cjs/react.js +3 -175
  7. package/dist/esm/components/MenuPanel.d.ts +13 -0
  8. package/dist/esm/index.d.ts +1 -3
  9. package/dist/esm/index.js +54 -125
  10. package/dist/esm/react.d.ts +1 -5
  11. package/dist/esm/react.js +3 -147
  12. package/package.json +2 -12
  13. package/scss/components/a11y-nav.scss +31 -0
  14. package/scss/components/button.scss +18 -54
  15. package/scss/components/header.scss +40 -0
  16. package/scss/components/logotype.scss +28 -0
  17. package/scss/components/mega-menu.scss +33 -0
  18. package/scss/components/menu-item.scss +40 -0
  19. package/scss/components/menu-panel.scss +32 -0
  20. package/scss/components/search.scss +3 -1
  21. package/scss/components/visually-hidden.scss +11 -0
  22. package/scss/tokens/colors.scss +0 -3
  23. package/scss/tokens/icons.scss +11 -10
  24. package/scss/tokens/spacing.scss +0 -65
  25. package/scss/utils/mixins.scss +5 -0
  26. package/scss/utils/prose.scss +10 -5
  27. package/scss/utils/reset.scss +35 -21
  28. package/dist/cjs/components/Button.d.ts +0 -20
  29. package/dist/cjs/components/ModalNavigationPanel.d.ts +0 -4
  30. package/dist/cjs/components/NavigationPanel.d.ts +0 -7
  31. package/dist/cjs/components/Radio.d.ts +0 -20
  32. package/dist/cjs/components/Select.d.ts +0 -34
  33. package/dist/cjs/components/Tabs.d.ts +0 -1
  34. package/dist/cjs/components/react/PersonalMenuPanel.d.ts +0 -19
  35. package/dist/cjs/components/react/Tabs.d.ts +0 -34
  36. package/dist/cjs/icons/400/ArrowBack.d.ts +0 -4
  37. package/dist/cjs/icons/400/Check.d.ts +0 -4
  38. package/dist/cjs/icons/500/ArrowForward.d.ts +0 -4
  39. package/dist/cjs/stories/Button.stories.d.ts +0 -14
  40. package/dist/cjs/stories/ContentTabs.stories.d.ts +0 -12
  41. package/dist/cjs/stories/FilterTabs.stories.d.ts +0 -12
  42. package/dist/cjs/stories/InlineIcons.stories.d.ts +0 -9
  43. package/dist/cjs/stories/Select.stories.d.ts +0 -14
  44. package/dist/esm/components/Button.d.ts +0 -20
  45. package/dist/esm/components/ModalNavigationPanel.d.ts +0 -4
  46. package/dist/esm/components/NavigationPanel.d.ts +0 -7
  47. package/dist/esm/components/Radio.d.ts +0 -20
  48. package/dist/esm/components/Select.d.ts +0 -34
  49. package/dist/esm/components/Tabs.d.ts +0 -1
  50. package/dist/esm/components/react/PersonalMenuPanel.d.ts +0 -19
  51. package/dist/esm/components/react/Tabs.d.ts +0 -34
  52. package/dist/esm/icons/400/ArrowBack.d.ts +0 -4
  53. package/dist/esm/icons/400/Check.d.ts +0 -4
  54. package/dist/esm/icons/500/ArrowForward.d.ts +0 -4
  55. package/dist/esm/stories/Button.stories.d.ts +0 -14
  56. package/dist/esm/stories/ContentTabs.stories.d.ts +0 -12
  57. package/dist/esm/stories/FilterTabs.stories.d.ts +0 -12
  58. package/dist/esm/stories/InlineIcons.stories.d.ts +0 -9
  59. package/dist/esm/stories/Select.stories.d.ts +0 -14
  60. package/scss/components/checkbox.scss +0 -63
  61. package/scss/components/details.scss +0 -40
  62. package/scss/components/fieldset.scss +0 -60
  63. package/scss/components/form-control.scss +0 -25
  64. package/scss/components/header-menu-item.scss +0 -38
  65. package/scss/components/input.scss +0 -21
  66. package/scss/components/main-header.scss +0 -105
  67. package/scss/components/select.scss +0 -46
package/dist/esm/index.js CHANGED
@@ -1,57 +1,3 @@
1
- /**
2
- * Close all <dialog>s (modal and non-modal) in a document
3
- */
4
- function closeAllDialogs$1() {
5
- document.querySelectorAll("dialog").forEach((dialog) => dialog.close());
6
- }
7
- /**
8
- * Add open/close functionality to a set of navigation panels
9
- *
10
- * @param container The element containing all the menus
11
- * @param items A list of all menu elements
12
- */
13
- function setNavigationPanels(container, items) {
14
- if (!container) {
15
- return;
16
- }
17
- for (const item of items) {
18
- if (!(item instanceof HTMLElement)) {
19
- continue;
20
- }
21
- const dialog = item.nextElementSibling;
22
- if (!(dialog instanceof HTMLDialogElement)) {
23
- continue;
24
- }
25
- dialog.addEventListener("keydown", (e) => {
26
- if (e.key === "Escape") {
27
- closeAllDialogs$1();
28
- }
29
- });
30
- const closeButton = dialog.querySelector(".close");
31
- if (closeButton instanceof HTMLButtonElement) {
32
- closeButton.addEventListener("click", () => {
33
- closeAllDialogs$1();
34
- });
35
- }
36
- item.addEventListener("click", (e) => {
37
- e.preventDefault();
38
- if (!dialog.open) {
39
- closeAllDialogs$1();
40
- dialog.show();
41
- }
42
- else {
43
- closeAllDialogs$1();
44
- }
45
- });
46
- }
47
- container.addEventListener("focusout", function (e) {
48
- const target = e.relatedTarget;
49
- if (target && target instanceof Node && !container.contains(target)) {
50
- closeAllDialogs$1();
51
- }
52
- });
53
- }
54
-
55
1
  /**
56
2
  * Close all <dialog>s (modal and non-modal) in a document
57
3
  */
@@ -59,88 +5,71 @@ function closeAllDialogs() {
59
5
  document.querySelectorAll("dialog").forEach((dialog) => dialog.close());
60
6
  }
61
7
  /**
62
- * Add open/close functionality to a set of navigation panels
8
+ * Add mouse and keyboard event listeners to a menu panel (<dialog>)
9
+ * @param dialog A <dialog> element
63
10
  */
64
- function setModalNavigationPanels(items) {
65
- for (const item of items) {
66
- if (!(item instanceof HTMLElement)) {
67
- continue;
11
+ function addEventListeners(dialog) {
12
+ const closeButton = dialog.querySelector(".kth-button.close");
13
+ const backButton = dialog.querySelector(".kth-button.back");
14
+ dialog.addEventListener("keydown", (e) => {
15
+ if (e.key === "Escape") {
16
+ dialog.close();
68
17
  }
69
- const dialog = item.nextElementSibling;
70
- if (!(dialog instanceof HTMLDialogElement)) {
71
- continue;
72
- }
73
- dialog.addEventListener("keydown", (e) => {
74
- e.stopPropagation();
75
- if (e.key === "Escape") {
76
- dialog.close();
77
- }
78
- });
79
- const closeButton = dialog.querySelector(".close");
80
- if (closeButton instanceof HTMLButtonElement) {
81
- closeButton.addEventListener("click", () => {
82
- closeAllDialogs();
83
- });
84
- }
85
- item.addEventListener("click", (e) => {
86
- e.preventDefault();
87
- // closeAllDialogs();
88
- dialog.showModal();
18
+ });
19
+ if (closeButton instanceof HTMLButtonElement) {
20
+ closeButton.addEventListener("click", () => {
21
+ closeAllDialogs();
89
22
  });
90
23
  }
91
- }
92
-
93
- function getPanel(tab) {
94
- const panelId = tab.getAttribute("aria-controls");
95
- if (!panelId) {
96
- throw new Error();
97
- }
98
- const panel = document.getElementById(panelId);
99
- if (!(panel instanceof HTMLElement)) {
100
- throw new Error();
101
- }
102
- return panel;
103
- }
104
- function closeAllTabs(tabs) {
105
- for (const tab of tabs) {
106
- if (tab instanceof HTMLElement) {
107
- tab.ariaSelected = "false";
108
- const panel = getPanel(tab);
109
- if (panel) {
110
- panel.hidden = true;
111
- }
112
- }
24
+ if (backButton instanceof HTMLButtonElement) {
25
+ backButton.addEventListener("click", () => {
26
+ dialog.close();
27
+ });
113
28
  }
114
29
  }
115
- function initTabs(container) {
116
- if (!(container instanceof HTMLElement)) {
117
- return;
118
- }
119
- const tablist = container.querySelector("[role=tablist]");
120
- if (!tablist) {
121
- return;
122
- }
123
- const tabs = tablist.querySelectorAll("[role=tab]");
124
- function openTab(tab) {
125
- const panel = getPanel(tab);
126
- if (!panel) {
30
+ class MenuPanel {
31
+ static init(container, items) {
32
+ if (!container)
127
33
  return;
34
+ for (const item of items) {
35
+ if (!(item instanceof HTMLElement))
36
+ continue;
37
+ const dialog = item.nextElementSibling;
38
+ if (!(dialog instanceof HTMLDialogElement))
39
+ continue;
40
+ addEventListeners(dialog);
41
+ item.addEventListener("click", (e) => {
42
+ e.preventDefault();
43
+ if (!dialog.open) {
44
+ closeAllDialogs();
45
+ dialog.show();
46
+ }
47
+ else {
48
+ closeAllDialogs();
49
+ }
50
+ });
128
51
  }
129
- tab.ariaSelected = "true";
130
- panel.hidden = false;
52
+ container.addEventListener("focusout", function (e) {
53
+ const target = e.relatedTarget;
54
+ if (target && target instanceof Node && !container.contains(target)) {
55
+ closeAllDialogs();
56
+ }
57
+ });
131
58
  }
132
- for (const tab of tabs) {
133
- if (tab instanceof HTMLElement) {
134
- tab.addEventListener("click", () => {
135
- closeAllTabs(tabs);
136
- openTab(tab);
59
+ static initModal(items) {
60
+ for (const item of items) {
61
+ if (!(item instanceof HTMLElement))
62
+ continue;
63
+ const dialog = item.nextElementSibling;
64
+ if (!(dialog instanceof HTMLDialogElement))
65
+ continue;
66
+ addEventListeners(dialog);
67
+ item.addEventListener("click", (e) => {
68
+ e.preventDefault();
69
+ dialog.showModal();
137
70
  });
138
71
  }
139
72
  }
140
- if (tabs[0] instanceof HTMLElement) {
141
- closeAllTabs(tabs);
142
- openTab(tabs[0]);
143
- }
144
73
  }
145
74
 
146
- export { initTabs, setModalNavigationPanels, setNavigationPanels };
75
+ export { MenuPanel };
@@ -1,5 +1 @@
1
- export { Tab, ContentTabs, NavigationTabs } from "./components/react/Tabs";
2
- export { Select, Option, OptionGroup } from "./components/Select";
3
- export { BackButton, Button, ForwardButton } from "./components/Button";
4
- export { default as IconArrowBack } from "./icons/400/ArrowBack";
5
- export { default as IconCheck } from "./icons/400/Check";
1
+ export default function hello(): string;
package/dist/esm/react.js CHANGED
@@ -1,149 +1,5 @@
1
- import * as React from 'react';
2
- import React__default from 'react';
3
-
4
- function getValueFromUrl(key) {
5
- const url = new URL(location.href);
6
- if (key) {
7
- return url.searchParams.get(key);
8
- }
9
- else {
10
- return url.hash === "" ? null : url.hash.slice(1);
11
- }
12
- }
13
- /**
14
- * Sync state with URL hash or query parameter.
15
- * @param defaultValue Default state value
16
- * @param key Query parameter to use. Hash if empty
17
- * @param enableUrl Will sync with URL if true (this function behaves the same
18
- * as `React.useState` if false)
19
- */
20
- function useUrlState(defaultValue = "", key = "", enableUrl = false) {
21
- const initialValue = (enableUrl && getValueFromUrl(key)) || defaultValue;
22
- const [value, _setValue] = React__default.useState(initialValue);
23
- React__default.useEffect(() => {
24
- function updateValue() {
25
- if (enableUrl) {
26
- _setValue(getValueFromUrl(key) || defaultValue);
27
- }
28
- }
29
- window.addEventListener("popstate", updateValue);
30
- return () => {
31
- window.removeEventListener("popstate", updateValue);
32
- };
33
- });
34
- function setValue(value) {
35
- _setValue(value);
36
- const url = new URL(location.href);
37
- if (key !== "") {
38
- url.searchParams.set(key, value);
39
- }
40
- else {
41
- url.hash = "#" + value;
42
- }
43
- if (enableUrl) {
44
- history.pushState(undefined, "", url);
45
- }
46
- }
47
- return [value, setValue];
48
- }
49
- function handleKeyDown(event, index, tabs, setActiveTab) {
50
- event.preventDefault();
51
- switch (event.key) {
52
- case "ArrowLeft":
53
- case "ArrowUp":
54
- if (index > 0) {
55
- setActiveTab(tabs[index - 1].props.id);
56
- }
57
- break;
58
- case "ArrowRight":
59
- case "ArrowDown":
60
- if (index < tabs.length - 1) {
61
- setActiveTab(tabs[index + 1].props.id);
62
- }
63
- break;
64
- case "Home":
65
- setActiveTab(tabs[0].props.id);
66
- break;
67
- case "End":
68
- setActiveTab(tabs[tabs.length - 1].props.id);
69
- break;
70
- }
71
- }
72
- function useFocus(element) {
73
- element?.scrollIntoView({ block: "nearest", inline: "nearest" });
74
- element?.focus();
75
- }
76
- /**
77
- * Highly customizable Tabs component made for testing purposes.
78
- * You should use {@link NavigationTabs} or {@link ContentTabs} instead.
79
- */
80
- function GenericTabs({ classPrefix, children, id, defaultValue, url = "none", }) {
81
- const ref = React__default.useRef(null);
82
- const [activeTab, setActiveTab] = useUrlState(defaultValue, url === "query" ? id : "", url === "query" || url === "hash");
83
- let activeTabIndex = children.findIndex((c) => c.props.id === activeTab);
84
- if (activeTabIndex === -1) {
85
- activeTabIndex = 0;
86
- }
87
- useFocus(ref.current?.querySelector(`#${activeTab}-tab`));
88
- return (React__default.createElement(React__default.Fragment, null,
89
- React__default.createElement("div", { ref: ref, className: `${classPrefix}`, id: id },
90
- React__default.createElement("ul", { className: `${classPrefix}__tablist`, role: "tablist" }, children.map((child, index) => (React__default.createElement("li", { role: "presentation", className: `${classPrefix}__tab-element` },
91
- React__default.createElement("button", { key: child.props.id, role: "tab", "aria-selected": index === activeTabIndex, "aria-controls": child.props.id, id: `${child.props.id}-tab`, className: `${classPrefix}__tab`, tabIndex: index === activeTabIndex ? 0 : -1, onKeyDown: (event) => {
92
- handleKeyDown(event, index, children, setActiveTab);
93
- }, onClick: (event) => {
94
- event.preventDefault();
95
- setActiveTab(child.props.id);
96
- } }, child.props.title)))))),
97
- children.map((child, index) => (React__default.createElement("div", { className: `${classPrefix}__tabpanel`, role: "tabpanel", "aria-labelledby": `${child.props.id}-tab`, id: child.props.id, key: index, hidden: index !== activeTabIndex }, child)))));
98
- }
99
- function NavigationTabs({ id, children, url = "query", defaultValue, }) {
100
- return (React__default.createElement(GenericTabs, { id: id, children: children, url: url, defaultValue: defaultValue, classPrefix: "kth-navigation-tabs" }));
101
- }
102
- function ContentTabs({ id, children, url = "hash", defaultValue, }) {
103
- return (React__default.createElement(GenericTabs, { id: id, children: children, url: url, defaultValue: defaultValue, classPrefix: "kth-content-tabs" }));
104
- }
105
- function Tab({ children }) {
106
- return React__default.createElement(React__default.Fragment, null, children);
107
- }
108
-
109
- function Select({ name, label, description, value, onChange, children, error, }) {
110
- const id = `select-${name}`;
111
- return (React__default.createElement("div", { className: "kth-select" },
112
- React__default.createElement("label", { htmlFor: id }, label),
113
- description && React__default.createElement("p", { id: `${id}-description` }, description),
114
- React__default.createElement("select", { id: id, "aria-describedby": description && `${id}-description`, name: name, value: value, onChange: (e) => onChange(e.target.value), "aria-invalid": error !== undefined ? "true" : "false" }, children),
115
- error && (React__default.createElement("p", { className: "kth-select__error", role: "alert" }, error))));
1
+ function hello() {
2
+ return "world";
116
3
  }
117
- function OptionGroup({ label, children }) {
118
- return React__default.createElement("optgroup", { label: label }, children);
119
- }
120
- function Option({ value, children }) {
121
- return React__default.createElement("option", { value: value }, children);
122
- }
123
-
124
- const SvgArrowBack = (props) => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1.25em", height: "1.25em", viewBox: "0 0 20 20", ...props },
125
- React.createElement("path", { fill: "currentColor", d: "m10 16-6-6 6-6 1.063 1.063L6.875 9.25H16v1.5H6.875l4.188 4.188L10 16Z" })));
126
-
127
- const SvgArrowForward = (props) => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1.25em", height: "1.25em", viewBox: "0 0 20 20", ...props },
128
- React.createElement("path", { fill: "currentColor", d: "m10 16.075-1.227-1.227 3.983-3.983h-8.83v-1.73h8.83L8.773 5.152 10 3.925 16.075 10 10 16.075Z" })));
129
-
130
- /** A button with just text */
131
- function Button({ children, onClick, appearance }) {
132
- const className = ["kth-button", appearance].join(" ");
133
- return (React__default.createElement("button", { onClick: onClick, className: className }, children));
134
- }
135
- function BackButton({ children, onClick }) {
136
- return (React__default.createElement("button", { onClick: onClick, className: "kth-button tertiary" },
137
- React__default.createElement(SvgArrowBack, null),
138
- children));
139
- }
140
- function ForwardButton({ children, onClick }) {
141
- return (React__default.createElement("button", { onClick: onClick, className: "kth-button primary" },
142
- children,
143
- React__default.createElement(SvgArrowForward, null)));
144
- }
145
-
146
- const SvgCheck = (props) => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1.25em", height: "1.25em", viewBox: "0 0 20 20", ...props },
147
- React.createElement("path", { fill: "currentColor", d: "M8.23 14.063 4.707 10.52 5.75 9.479l2.48 2.459 6.02-6L15.292 7l-7.063 7.063Z" })));
148
4
 
149
- export { BackButton, Button, ContentTabs, ForwardButton, SvgArrowBack as IconArrowBack, SvgCheck as IconCheck, NavigationTabs, Option, OptionGroup, Select, Tab };
5
+ export { hello as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kth/style",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "exports": {
@@ -16,7 +16,6 @@
16
16
  },
17
17
  "scripts": {
18
18
  "clean": "rm -rf dist",
19
- "dev": "storybook dev -p 6006",
20
19
  "build": "npm run clean && npm run build:components",
21
20
  "build:components": "rollup -c",
22
21
  "test": "npm run test:lint",
@@ -30,13 +29,6 @@
30
29
  "@rollup/plugin-commonjs": "^24.1.0",
31
30
  "@rollup/plugin-node-resolve": "^15.0.2",
32
31
  "@rollup/plugin-typescript": "^11.1.0",
33
- "@storybook/addon-essentials": "^7.0.6",
34
- "@storybook/addon-interactions": "^7.0.6",
35
- "@storybook/addon-links": "^7.0.6",
36
- "@storybook/blocks": "^7.0.6",
37
- "@storybook/react": "^7.0.6",
38
- "@storybook/react-vite": "^7.0.6",
39
- "@storybook/testing-library": "^0.0.14-next.2",
40
32
  "@types/react": "^18.0.28",
41
33
  "@types/react-dom": "^18.0.11",
42
34
  "@typescript-eslint/eslint-plugin": "^5.57.1",
@@ -45,17 +37,15 @@
45
37
  "eslint": "^8.38.0",
46
38
  "eslint-plugin-react-hooks": "^4.6.0",
47
39
  "eslint-plugin-react-refresh": "^0.3.4",
48
- "eslint-plugin-storybook": "^0.6.11",
49
40
  "prop-types": "^15.8.1",
50
41
  "react": "^18.2.0",
51
42
  "react-dom": "^18.2.0",
52
43
  "rollup": "^3.21.4",
53
- "storybook": "^7.0.6",
54
44
  "typescript": "^5.0.2",
55
45
  "vite": "^4.3.0"
56
46
  },
57
47
  "peerDependencies": {
58
48
  "react": "*"
59
49
  },
60
- "gitHead": "8768a00294cdeeb082cf4b043f192dc64aaec5fb"
50
+ "gitHead": "2ef5d86944a2cd3c4eee7011c4b71983e11f309f"
61
51
  }
@@ -0,0 +1,31 @@
1
+ @use "../tokens/colors.scss" as c;
2
+ @use "../tokens/spacing.scss" as s;
3
+
4
+ @layer kth-style {
5
+ .kth-a11y-nav {
6
+ position: fixed;
7
+ margin: s.$space-16;
8
+ padding: s.$space-8;
9
+ background: var(--color-background);
10
+ color: var(--color-text);
11
+ border-radius: s.$space-4;
12
+
13
+ // TODO: move those border styles to design tokens
14
+ // TODO: create tokens for shadows
15
+ border-width: calc(1rem / 16);
16
+ border-color: var(--color-text);
17
+ border-style: solid;
18
+
19
+ box-shadow: 0 2px 4px 0 rgb(33, 33, 33, 0.4);
20
+
21
+ // Visually hidden by default
22
+ z-index: -1;
23
+ left: -999999px;
24
+
25
+ &:focus-within {
26
+ // Show when something has focus
27
+ left: 0;
28
+ z-index: 100000;
29
+ }
30
+ }
31
+ }
@@ -1,62 +1,26 @@
1
1
  @use "../tokens/spacing";
2
2
  @use "../tokens/icons";
3
- @use "sass:math";
4
-
5
- // TODO: move to its own tokens
6
- $border-radius: spacing.$space-4;
7
- $secondary-border-width: math.div(1rem, 16);
8
3
 
9
4
  @layer kth-style {
10
- .kth-button {
11
- border-radius: $border-radius;
5
+ .kth-button.close {
12
6
  display: flex;
13
- align-items: center;
14
- gap: spacing.$space-4;
15
-
16
- &.primary,
17
- &.success,
18
- &.danger,
19
- &.next {
20
- color: var(--color-on-primary);
21
- border: none;
22
- padding-block: var(--space-inner-block);
23
- padding-inline: var(--space-inner-inline);
24
- font: var(--font-label-s);
25
- }
26
-
27
- &.primary,
28
- &.next {
29
- background: var(--color-primary);
30
- }
31
-
32
- &.success {
33
- background: var(--color-success, green);
34
- }
35
-
36
- &.danger {
37
- background: var(--color-error, red);
38
- }
39
-
40
- &.secondary,
41
- &.previous {
42
- --border-width: #{$secondary-border-width};
43
- border-color: var(--color-secondary);
44
- border-width: var(--border-width);
45
- border-style: solid;
46
-
47
- background: transparent;
48
- padding-block: calc(var(--space-inner-block) - var(--border-width));
49
- padding-inline: calc(var(--space-inner-inline) - var(--border-width));
50
- }
51
-
52
- &.next::after {
53
- @include icons.icon-arrow-right-500;
54
- background-color: var(--color-on-primary);
55
- }
7
+ margin-inline-start: auto;
8
+ background: transparent;
9
+ border: none;
10
+ padding-inline: 0;
11
+ padding-block: spacing.$space-8;
12
+ }
56
13
 
57
- &.previous::before {
58
- @include icons.icon-arrow-left-300;
59
- background-color: var(--color-secondary);
60
- }
14
+ .kth-button.close::before {
15
+ content: "";
16
+ display: inline-block;
17
+ width: 1.5rem;
18
+ height: 1.5rem;
19
+ background-color: var(--color-primary);
20
+
21
+ mask-image: icons.$icon-url-close;
22
+ -webkit-mask-image: icons.$icon-url-close;
23
+ mask-size: cover;
24
+ -webkit-mask-size: cover;
61
25
  }
62
26
  }
@@ -0,0 +1,40 @@
1
+ @use "../tokens/colors.scss";
2
+ @use "../tokens/spacing.scss";
3
+ @use "../utils/mixins.scss";
4
+
5
+ @layer kth-style {
6
+ .kth-header.intranet {
7
+ @include colors.theme-intranet;
8
+ }
9
+ .kth-header.external {
10
+ @include colors.theme-inverse;
11
+ }
12
+ .kth-header.student-web {
13
+ @include colors.theme-student-web;
14
+ }
15
+
16
+ .kth-header {
17
+ color: var(--color-text);
18
+ background: var(--color-background);
19
+ padding-block: spacing.$space-16;
20
+ position: relative;
21
+
22
+ &__container {
23
+ @include mixins.container;
24
+ display: flex;
25
+ align-items: center;
26
+ }
27
+
28
+ &__tools {
29
+ @include mixins.horizontal-list();
30
+ flex-wrap: wrap;
31
+ gap: 0 spacing.$space-16;
32
+ justify-content: flex-end;
33
+ margin-inline-start: auto;
34
+ }
35
+
36
+ &__mega-menu--collapsable {
37
+ display: none;
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,28 @@
1
+ @use "../tokens/spacing.scss";
2
+
3
+ @layer kth-style {
4
+ a.kth-logotype,
5
+ figure.kth-logotype {
6
+ display: inline-block;
7
+ block-size: 4rem;
8
+ inline-size: 4rem;
9
+ border: none;
10
+ margin: 0;
11
+ margin-inline-end: spacing.$space-32;
12
+ padding: 0;
13
+
14
+ // Defensive measurement:
15
+ // - We prevent logo from shrinking
16
+ flex: 0 0 auto;
17
+
18
+ > figure {
19
+ block-size: 100%;
20
+ inline-size: 100%;
21
+ }
22
+
23
+ img {
24
+ block-size: 100%;
25
+ inline-size: 100%;
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,33 @@
1
+ @use "../tokens/spacing.scss";
2
+ @use "../utils/mixins.scss";
3
+
4
+ // TODO: this should be a token
5
+ $breakpoint-mega-menu: 64rem;
6
+
7
+ @layer kth-style {
8
+ nav.kth-mega-menu {
9
+ > ul {
10
+ @include mixins.horizontal-list();
11
+
12
+ // Defensive measurements:
13
+ // - `flex-wrap: wrap` makes sure that all menus are displayed decently
14
+ // - `0` in gap (block axis) makes sure that menu spacing is ok all the time
15
+ flex-wrap: wrap;
16
+ gap: 0 spacing.$space-16;
17
+ }
18
+ }
19
+
20
+ nav.kth-mega-menu--collapsable {
21
+ display: none;
22
+ }
23
+
24
+ @media (max-width: $breakpoint-mega-menu) {
25
+ nav.kth-mega-menu {
26
+ display: none;
27
+ }
28
+
29
+ nav.kth-mega-menu--collapsable {
30
+ display: block;
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,40 @@
1
+ @use "../tokens/spacing";
2
+ @use "../tokens/icons";
3
+
4
+ @layer kth-style {
5
+ .kth-menu-item {
6
+ color: var(--color-primary);
7
+ text-decoration: none;
8
+ display: flex;
9
+ align-items: center;
10
+ gap: spacing.$space-4;
11
+
12
+ // Fixes for buttons:
13
+ border: none;
14
+ background: none;
15
+ padding: 0;
16
+
17
+ span {
18
+ padding-block: spacing.$space-8;
19
+ padding-block-end: spacing.$space-4;
20
+ border-bottom: transparent spacing.$space-4 solid;
21
+ }
22
+
23
+ &:hover span {
24
+ border-color: var(--color-primary);
25
+ }
26
+
27
+ &.dropdown::after {
28
+ @include icons.icon-caret-down;
29
+ background-color: var(--color-primary);
30
+ }
31
+ &.search::before {
32
+ @include icons.icon-search;
33
+ background-color: var(--color-primary);
34
+ }
35
+ &.language::before {
36
+ @include icons.icon-globe;
37
+ background-color: var(--color-primary);
38
+ }
39
+ }
40
+ }