diff --git a/@codexteam/ui/package.json b/@codexteam/ui/package.json
index 70c7ea3f..1f69aaa1 100644
--- a/@codexteam/ui/package.json
+++ b/@codexteam/ui/package.json
@@ -54,6 +54,7 @@
},
"devDependencies": {
"@codexteam/icons": "^0.3.3",
+ "@csstools/postcss-global-data": "^2.1.1",
"@editorjs/header": "^2.8.6",
"@editorjs/nested-list": "^1.4.2",
"@types/node": "^20.11.15",
diff --git a/@codexteam/ui/postcss.config.js b/@codexteam/ui/postcss.config.js
index 6f1ee2c6..8333ef19 100644
--- a/@codexteam/ui/postcss.config.js
+++ b/@codexteam/ui/postcss.config.js
@@ -1,3 +1,5 @@
+import { fileURLToPath } from 'url';
+import postcssGlobalData from '@csstools/postcss-global-data';
import postcssNested from 'postcss-nested';
import postcssPresetEnv from 'postcss-preset-env';
import postcssApply from 'postcss-apply';
@@ -10,6 +12,12 @@ import postcssHoverMediaFeature from 'postcss-hover-media-feature';
*/
export default function () {
return {
- plugins: [postcssNested(), postcssPresetEnv(), postcssApply(), postcssHoverMediaFeature()],
+ plugins: [
+ postcssGlobalData({ files: [fileURLToPath(new URL('./src/styles/breakpoints.pcss', import.meta.url))] }),
+ postcssNested(),
+ postcssPresetEnv(),
+ postcssApply(),
+ postcssHoverMediaFeature(),
+ ],
};
}
diff --git a/@codexteam/ui/src/styles/breakpoints.pcss b/@codexteam/ui/src/styles/breakpoints.pcss
new file mode 100644
index 00000000..2d24d834
--- /dev/null
+++ b/@codexteam/ui/src/styles/breakpoints.pcss
@@ -0,0 +1,4 @@
+@custom-media --mobile (max-width: 768px);
+
+/* 700px content plus two 300px sidebars */
+@custom-media --narrow (max-width: 1300px);
diff --git a/@codexteam/ui/src/styles/dimensions.pcss b/@codexteam/ui/src/styles/dimensions.pcss
index fa0b3646..07c60d9a 100644
--- a/@codexteam/ui/src/styles/dimensions.pcss
+++ b/@codexteam/ui/src/styles/dimensions.pcss
@@ -18,6 +18,7 @@
*/
--size-icon: 20px;
--size-avatar: 30px;
+ --size-cover: 150px;
/**
* Layout
@@ -25,6 +26,7 @@
--layout-content-width: 700px;
--layout-sidebar-width: 300px;
--layout-navbar-height: 51px;
+ --layout-text-width-min: 120px;
/**
* Height of the line between list items
@@ -48,6 +50,12 @@
--radius-field: var(--radius-m);
}
+@media (--mobile) {
+ :root {
+ --size-cover: 96px;
+ }
+}
+
[data-dimensions="small"] {
--v-padding: var(--spacing-xs);
--h-padding: var(--spacing-ms);
diff --git a/@codexteam/ui/src/styles/typography.pcss b/@codexteam/ui/src/styles/typography.pcss
index 195f0140..f7e63620 100644
--- a/@codexteam/ui/src/styles/typography.pcss
+++ b/@codexteam/ui/src/styles/typography.pcss
@@ -27,12 +27,15 @@
*/
font-size: 15px;
+ /* iOS Safari zooms into focused fields below 16px */
+ --font-size-field-mobile: 16px;
+
/**
* Content styles
* Should be used only for content (user-written text), not for UI elements.
*/
--text-h1 {
- font-size: 2.8rem;
+ font-size: clamp(2rem, 1.17rem + 3.17vw, 2.8rem);
line-height: 110%;
font-weight: 700;
margin: 0;
@@ -41,7 +44,7 @@
--text-h2 {
font-weight: 700;
line-height: 120%;
- font-size: 1.8rem;
+ font-size: clamp(1.5rem, 1.19rem + 1.19vw, 1.8rem);
}
--text-h3 {
diff --git a/@codexteam/ui/src/vue/components/card/Card.vue b/@codexteam/ui/src/vue/components/card/Card.vue
index 93bfafb3..37caab26 100644
--- a/@codexteam/ui/src/vue/components/card/Card.vue
+++ b/@codexteam/ui/src/vue/components/card/Card.vue
@@ -28,7 +28,12 @@
{{ subtitle }}
-
+
+
+
@@ -79,8 +84,6 @@ withDefaults(
diff --git a/@codexteam/ui/src/vue/components/popup/Popup.vue b/@codexteam/ui/src/vue/components/popup/Popup.vue
index 2cb38604..fcf198bc 100644
--- a/@codexteam/ui/src/vue/components/popup/Popup.vue
+++ b/@codexteam/ui/src/vue/components/popup/Popup.vue
@@ -94,6 +94,27 @@ withDefaults(
color: var(--base--text);
}
}
+
+ @media (--mobile) {
+ grid-template-columns: minmax(0, 1fr);
+ justify-items: center;
+ padding: var(--spacing-ml);
+ overflow-y: auto;
+
+ &__container {
+ grid-column: 1;
+ grid-row: 2;
+ max-width: 100%;
+ box-sizing: border-box;
+ }
+
+ &__icon {
+ grid-column: 1;
+ grid-row: 1;
+ justify-self: end;
+ margin: 0 0 var(--spacing-s);
+ }
+ }
}
diff --git a/@codexteam/ui/src/vue/components/row/Row.vue b/@codexteam/ui/src/vue/components/row/Row.vue
index 1f84e7e8..f02517ae 100644
--- a/@codexteam/ui/src/vue/components/row/Row.vue
+++ b/@codexteam/ui/src/vue/components/row/Row.vue
@@ -103,6 +103,7 @@ defineProps<{
&__center {
flex: 1;
+ min-width: 0;
display: flex;
flex-direction: column;
gap: var(--spacing-very-x);
@@ -112,5 +113,19 @@ defineProps<{
display: flex;
align-items: center;
}
+
+ @media (--mobile) {
+ &__body-inner {
+ flex-wrap: wrap;
+ }
+
+ &__center {
+ flex-basis: var(--layout-text-width-min);
+ }
+
+ &__right {
+ margin-left: auto;
+ }
+ }
}
diff --git a/@codexteam/ui/src/vue/components/tabbar/Tab.vue b/@codexteam/ui/src/vue/components/tabbar/Tab.vue
index 28952bc3..439f6d25 100644
--- a/@codexteam/ui/src/vue/components/tabbar/Tab.vue
+++ b/@codexteam/ui/src/vue/components/tabbar/Tab.vue
@@ -43,6 +43,7 @@
diff --git a/@codexteam/ui/src/vue/components/vertical-menu/VerticalMenu.vue b/@codexteam/ui/src/vue/components/vertical-menu/VerticalMenu.vue
index 4c61e412..315d08dd 100644
--- a/@codexteam/ui/src/vue/components/vertical-menu/VerticalMenu.vue
+++ b/@codexteam/ui/src/vue/components/vertical-menu/VerticalMenu.vue
@@ -29,5 +29,9 @@ defineProps<{
gap: var(--spacing-very-x);
background-color: var(--base--bg-secondary);
width: max-content;
+
+ @media (--mobile) {
+ width: auto;
+ }
}
diff --git a/@codexteam/ui/src/vue/composables/useViewport/index.ts b/@codexteam/ui/src/vue/composables/useViewport/index.ts
new file mode 100644
index 00000000..ef00cd1b
--- /dev/null
+++ b/@codexteam/ui/src/vue/composables/useViewport/index.ts
@@ -0,0 +1,8 @@
+import { createSharedComposable, useMediaQuery } from '@vueuse/core';
+
+// Keep in sync with --mobile in styles/breakpoints.pcss
+const MOBILE_MEDIA_QUERY = '(max-width: 768px)';
+
+export const useViewport = createSharedComposable(() => ({
+ isMobile: useMediaQuery(MOBILE_MEDIA_QUERY),
+}));
diff --git a/@codexteam/ui/src/vue/index.ts b/@codexteam/ui/src/vue/index.ts
index 4cd7c227..37f0c6b0 100644
--- a/@codexteam/ui/src/vue/index.ts
+++ b/@codexteam/ui/src/vue/index.ts
@@ -19,6 +19,7 @@ export * from './components/popover';
export * from './components/popup';
export * from './components/confirm';
export * from './composables/useTheme';
+export * from './composables/useViewport';
export * from './components/checkbox';
export * from './components/select';
export * from './components/chart';
diff --git a/@codexteam/ui/src/vue/layout/navbar/Navbar.vue b/@codexteam/ui/src/vue/layout/navbar/Navbar.vue
index 328e1f97..3a4212d8 100644
--- a/@codexteam/ui/src/vue/layout/navbar/Navbar.vue
+++ b/@codexteam/ui/src/vue/layout/navbar/Navbar.vue
@@ -26,5 +26,9 @@
&__right {
margin-left: auto;
}
+
+ @media (--mobile) {
+ padding: var(--spacing-ms) var(--spacing-m);
+ }
}
diff --git a/@codexteam/ui/src/vue/layout/page-block/PageBlock.vue b/@codexteam/ui/src/vue/layout/page-block/PageBlock.vue
index 3d5c382f..1b07029a 100644
--- a/@codexteam/ui/src/vue/layout/page-block/PageBlock.vue
+++ b/@codexteam/ui/src/vue/layout/page-block/PageBlock.vue
@@ -79,4 +79,26 @@ defineProps({
padding: var(--spacing-xxl) var(--spacing-ml);
}
}
+
+@media (--narrow) {
+ .page-block__sidebar:empty {
+ display: none;
+ }
+}
+
+@media (--mobile) {
+ .page-block {
+ flex-direction: column;
+ }
+
+ .page-block__content {
+ max-width: none;
+ padding: var(--spacing-l) var(--spacing-ml);
+ }
+
+ .page-block__sidebar {
+ width: auto;
+ padding: var(--spacing-l) var(--spacing-ml) 0;
+ }
+}
diff --git a/package.json b/package.json
index 4d005a77..0aad1ab5 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"vue-router": "^4.2.4"
},
"devDependencies": {
+ "@csstools/postcss-global-data": "^2.1.1",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.3.0",
"@hawk.so/vite-plugin": "^1.0.2",
diff --git a/postcss.config.js b/postcss.config.js
index 6f1ee2c6..025beb13 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,3 +1,5 @@
+import { fileURLToPath } from 'url';
+import postcssGlobalData from '@csstools/postcss-global-data';
import postcssNested from 'postcss-nested';
import postcssPresetEnv from 'postcss-preset-env';
import postcssApply from 'postcss-apply';
@@ -10,6 +12,12 @@ import postcssHoverMediaFeature from 'postcss-hover-media-feature';
*/
export default function () {
return {
- plugins: [postcssNested(), postcssPresetEnv(), postcssApply(), postcssHoverMediaFeature()],
+ plugins: [
+ postcssGlobalData({ files: [fileURLToPath(new URL('./@codexteam/ui/src/styles/breakpoints.pcss', import.meta.url))] }),
+ postcssNested(),
+ postcssPresetEnv(),
+ postcssApply(),
+ postcssHoverMediaFeature(),
+ ],
};
}
diff --git a/src/App.vue b/src/App.vue
index 70751eb9..9e8c495d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -46,5 +46,6 @@ body {
-moz-osx-font-smoothing: grayscale;
display: grid;
grid-template-rows: auto 1fr;
+ grid-template-columns: minmax(0, 1fr);
}
diff --git a/src/presentation/components/note-header/NoteHeader.vue b/src/presentation/components/note-header/NoteHeader.vue
index 1d26df61..48b570c8 100644
--- a/src/presentation/components/note-header/NoteHeader.vue
+++ b/src/presentation/components/note-header/NoteHeader.vue
@@ -21,6 +21,7 @@
opacity: var(--opacity);
transition: opacity 100ms;
justify-content: space-between;
+ gap: var(--spacing-s);
padding: var(--spacing-s) var(--spacing-m);
position: sticky;
top: var(--layout-navbar-height);
@@ -28,11 +29,25 @@
z-index: var(--z-note-header);
&__left {
+ flex: 1 1 0;
+ min-width: 0;
color: var(--base--text-secondary);
+
+ @media (--mobile) {
+ overflow-x: auto;
+ white-space: nowrap;
+ scrollbar-width: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
}
&__right {
display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
gap: var(--spacing-s);
align-items: center;
}
diff --git a/src/presentation/components/note-list/NoteList.vue b/src/presentation/components/note-list/NoteList.vue
index b70626aa..23e44fcf 100644
--- a/src/presentation/components/note-list/NoteList.vue
+++ b/src/presentation/components/note-list/NoteList.vue
@@ -86,12 +86,26 @@ function getSubtitle(note: Note): string | undefined {
diff --git a/src/presentation/pages/Note.vue b/src/presentation/pages/Note.vue
index d04c68a5..f2c839b9 100644
--- a/src/presentation/pages/Note.vue
+++ b/src/presentation/pages/Note.vue
@@ -44,7 +44,10 @@
@@ -65,7 +73,7 @@