@morphemecms/cms 0.7.0 → 0.8.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 (72) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/defaults/collections.ts +56 -0
  3. package/defaults/company.ts +13 -4
  4. package/defaults/texts.ts +17 -0
  5. package/icons/lucide/LICENSE-phosphor +21 -0
  6. package/icons/lucide/facebook-logo.svg +1 -0
  7. package/icons/lucide/linkedin-logo.svg +1 -0
  8. package/icons/lucide/pack.json +16 -1
  9. package/icons/lucide/pinterest-logo.svg +1 -0
  10. package/icons/lucide/x-logo.svg +1 -0
  11. package/icons/material-symbols/LICENSE-phosphor +21 -0
  12. package/icons/material-symbols/facebook-logo.svg +1 -0
  13. package/icons/material-symbols/linkedin-logo.svg +1 -0
  14. package/icons/material-symbols/pack.json +15 -0
  15. package/icons/material-symbols/pinterest-logo.svg +1 -0
  16. package/icons/material-symbols/x-logo.svg +1 -0
  17. package/icons/phosphor/facebook-logo.svg +1 -0
  18. package/icons/phosphor/linkedin-logo.svg +1 -0
  19. package/icons/phosphor/pinterest-logo.svg +1 -0
  20. package/icons/phosphor/x-logo.svg +1 -0
  21. package/lib/lead.d.mts +5 -0
  22. package/lib/lead.mjs +10 -0
  23. package/lib/lead.test.mjs +65 -0
  24. package/lib/licenses.mjs +1 -1
  25. package/package.json +2 -1
  26. package/scripts/icon-packs.mjs +51 -13
  27. package/scripts/material-fallback.test.mjs +49 -0
  28. package/site/icons.d.mts +13 -0
  29. package/site/icons.mjs +25 -3
  30. package/site/materialSymbols.d.mts +8 -2
  31. package/site/materialSymbols.mjs +27 -11
  32. package/site/materialSymbols.test.mjs +44 -0
  33. package/site/schema.ts +2 -0
  34. package/site/site.ts +9 -2
  35. package/site/social.d.mts +37 -0
  36. package/site/social.mjs +92 -0
  37. package/site/social.test.mjs +80 -0
  38. package/site/theme.d.mts +2 -0
  39. package/site/theme.mjs +28 -11
  40. package/site/themeImport.test.mjs +40 -0
  41. package/src/EntryForm.tsx +21 -58
  42. package/src/FormFrame.tsx +5 -5
  43. package/src/Parts.tsx +8 -30
  44. package/src/Screen.tsx +12 -5
  45. package/src/editor.css +70 -5
  46. package/src/i18n/da-DK/parts.ts +1 -1
  47. package/src/i18n/da-DK/theme.ts +4 -0
  48. package/src/i18n/en-US/parts.ts +1 -1
  49. package/src/i18n/en-US/theme.ts +4 -0
  50. package/src/icon.tsx +1 -1
  51. package/src/materialIcons.ts +5 -7
  52. package/src/pagePreview.tsx +21 -1
  53. package/src/postLead.d.mts +7 -0
  54. package/src/postLead.mjs +9 -0
  55. package/src/postLead.test.mjs +28 -0
  56. package/src/ui.tsx +41 -96
  57. package/src/virtual.d.ts +5 -0
  58. package/starter/package.json +1 -1
  59. package/starter/src/data/social.json +14 -0
  60. package/theme/components/PostAuthor.astro +20 -0
  61. package/theme/components/PostCard.tsx +26 -0
  62. package/theme/components/PostHeader.astro +36 -0
  63. package/theme/components/PostMeta.tsx +27 -2
  64. package/theme/components/RelatedPosts.astro +29 -0
  65. package/theme/components/ShareRow.astro +48 -0
  66. package/theme/layouts/PostLayout.astro +28 -54
  67. package/theme/styles/base.css +164 -127
  68. package/theme/styles/blocks.css +160 -155
  69. package/theme/styles/components.css +43 -34
  70. package/theme/styles/global.css +316 -303
  71. package/theme/styles/ui.css +43 -34
  72. package/theme/ui/copyLink.script.ts +17 -0
@@ -1,4 +1,7 @@
1
1
  /*
2
+ Material is every theme's fallback: its rules that set more than custom properties sit in @layer fallback (declared last in
3
+ base.css), so a theme's own unlayered rules win over them, whatever the specificity. Custom properties stay unlayered.
4
+
2
5
  The look of the standard blocks (their fields: packages/editor/src/standard/; their markup: blocks/ in this theme), on
3
6
  the site and in the editor, in the standard theme. Every colour, shape and motion comes from a CSS variable, which a
4
7
  theme sets to its own design in its own CSS:
@@ -1714,182 +1717,184 @@
1714
1717
  }
1715
1718
  }
1716
1719
 
1717
- /* "Video": the video before it is played (video/, packages/editor/lib/video.mjs); the video takes its
1720
+ @layer fallback {
1721
+ /* "Video": the video before it is played (video/, packages/editor/lib/video.mjs); the video takes its
1718
1722
  place. Outside the layers, as it is a link in the text's style (.prose), whose link style it undoes. */
1719
- .prose .video-facade {
1720
- display: flex;
1721
- flex-direction: column;
1722
- align-items: center;
1723
- justify-content: center;
1724
- gap: 0.5rem;
1725
- width: 100%;
1726
- aspect-ratio: 16 / 9;
1727
- margin-block: 2em;
1728
- padding: 1.5rem;
1729
- border-radius: var(--block-radius);
1730
- background: var(--block-tint);
1731
- color: var(--block-text);
1732
- text-align: center;
1733
- text-decoration: none;
1734
- }
1735
- .prose .video-facade svg {
1736
- color: var(--block-accent);
1737
- transition: transform var(--motion-spatial);
1738
- }
1739
- .prose .video-facade:hover svg {
1740
- transform: scale(1.08);
1741
- }
1742
- /* At most two lines; a longer title ends with "…" (the whole title is still the link's name). */
1743
- .video-facade-title {
1744
- display: -webkit-box;
1745
- max-width: 40ch;
1746
- overflow: hidden;
1747
- font-size: var(--type-title-medium-size);
1748
- line-height: var(--type-title-medium-line-height);
1749
- font-weight: var(--type-title-medium-weight);
1750
- letter-spacing: var(--type-title-medium-tracking);
1751
- -webkit-box-orient: vertical;
1752
- -webkit-line-clamp: 2;
1753
- line-clamp: 2;
1754
- }
1755
- /* With a still from the video (packages/editor/scripts/video-thumbnails.mjs), as on YouTube: the still fills the
1756
- box, the white play button in the middle of the box (not of the space above the title), the title
1757
- at the bottom over a shade. */
1758
- .prose .video-facade-still {
1759
- position: relative;
1760
- isolation: isolate;
1761
- overflow: hidden;
1762
- align-items: flex-start;
1763
- justify-content: flex-end;
1764
- gap: 0.25rem;
1765
- padding: 1rem 1.25rem;
1766
- background: #000;
1767
- color: #fff;
1768
- text-align: left;
1769
- }
1770
- .prose .video-facade-still::after {
1771
- content: '';
1772
- position: absolute;
1773
- inset: 0;
1774
- z-index: -1;
1775
- background: linear-gradient(to bottom, rgb(0 0 0 / 0.15) 40%, rgb(0 0 0 / 0.75));
1776
- }
1777
- .prose .video-facade-still .video-facade-image {
1778
- position: absolute;
1779
- inset: 0;
1780
- z-index: -2;
1781
- width: 100%;
1782
- height: 100%;
1783
- margin: 0;
1784
- object-fit: cover;
1785
- }
1786
- .prose .video-facade-still svg {
1787
- position: absolute;
1788
- inset: 0;
1789
- margin: auto;
1790
- color: #fff;
1791
- filter: drop-shadow(0 2px 8px rgb(0 0 0 / 0.5));
1792
- }
1793
- .video-facade-still .video-facade-title {
1794
- font-size: var(--type-title-small-size);
1795
- line-height: var(--type-title-small-line-height);
1796
- font-weight: var(--type-title-small-weight);
1797
- letter-spacing: var(--type-title-small-tracking);
1798
- color: #fff;
1799
- text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
1800
- }
1801
- @media (width >= theme(--breakpoint-medium)) {
1802
- .prose .video-facade-still svg {
1803
- width: 4.5rem;
1804
- height: 4.5rem;
1723
+ .prose .video-facade {
1724
+ display: flex;
1725
+ flex-direction: column;
1726
+ align-items: center;
1727
+ justify-content: center;
1728
+ gap: 0.5rem;
1729
+ width: 100%;
1730
+ aspect-ratio: 16 / 9;
1731
+ margin-block: 2em;
1732
+ padding: 1.5rem;
1733
+ border-radius: var(--block-radius);
1734
+ background: var(--block-tint);
1735
+ color: var(--block-text);
1736
+ text-align: center;
1737
+ text-decoration: none;
1805
1738
  }
1806
- .video-facade-still .video-facade-title {
1739
+ .prose .video-facade svg {
1740
+ color: var(--block-accent);
1741
+ transition: transform var(--motion-spatial);
1742
+ }
1743
+ .prose .video-facade:hover svg {
1744
+ transform: scale(1.08);
1745
+ }
1746
+ /* At most two lines; a longer title ends with "…" (the whole title is still the link's name). */
1747
+ .video-facade-title {
1748
+ display: -webkit-box;
1749
+ max-width: 40ch;
1750
+ overflow: hidden;
1807
1751
  font-size: var(--type-title-medium-size);
1808
1752
  line-height: var(--type-title-medium-line-height);
1809
1753
  font-weight: var(--type-title-medium-weight);
1810
1754
  letter-spacing: var(--type-title-medium-tracking);
1755
+ -webkit-box-orient: vertical;
1756
+ -webkit-line-clamp: 2;
1757
+ line-clamp: 2;
1758
+ }
1759
+ /* With a still from the video (packages/editor/scripts/video-thumbnails.mjs), as on YouTube: the still fills the
1760
+ box, the white play button in the middle of the box (not of the space above the title), the title
1761
+ at the bottom over a shade. */
1762
+ .prose .video-facade-still {
1763
+ position: relative;
1764
+ isolation: isolate;
1765
+ overflow: hidden;
1766
+ align-items: flex-start;
1767
+ justify-content: flex-end;
1768
+ gap: 0.25rem;
1769
+ padding: 1rem 1.25rem;
1770
+ background: #000;
1771
+ color: #fff;
1772
+ text-align: left;
1773
+ }
1774
+ .prose .video-facade-still::after {
1775
+ content: '';
1776
+ position: absolute;
1777
+ inset: 0;
1778
+ z-index: -1;
1779
+ background: linear-gradient(to bottom, rgb(0 0 0 / 0.15) 40%, rgb(0 0 0 / 0.75));
1780
+ }
1781
+ .prose .video-facade-still .video-facade-image {
1782
+ position: absolute;
1783
+ inset: 0;
1784
+ z-index: -2;
1785
+ width: 100%;
1786
+ height: 100%;
1787
+ margin: 0;
1788
+ object-fit: cover;
1789
+ }
1790
+ .prose .video-facade-still svg {
1791
+ position: absolute;
1792
+ inset: 0;
1793
+ margin: auto;
1794
+ color: #fff;
1795
+ filter: drop-shadow(0 2px 8px rgb(0 0 0 / 0.5));
1796
+ }
1797
+ .video-facade-still .video-facade-title {
1798
+ font-size: var(--type-title-small-size);
1799
+ line-height: var(--type-title-small-line-height);
1800
+ font-weight: var(--type-title-small-weight);
1801
+ letter-spacing: var(--type-title-small-tracking);
1802
+ color: #fff;
1803
+ text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
1804
+ }
1805
+ @media (width >= theme(--breakpoint-medium)) {
1806
+ .prose .video-facade-still svg {
1807
+ width: 4.5rem;
1808
+ height: 4.5rem;
1809
+ }
1810
+ .video-facade-still .video-facade-title {
1811
+ font-size: var(--type-title-medium-size);
1812
+ line-height: var(--type-title-medium-line-height);
1813
+ font-weight: var(--type-title-medium-weight);
1814
+ letter-spacing: var(--type-title-medium-tracking);
1815
+ }
1811
1816
  }
1812
- }
1813
1817
 
1814
- /* A block's head with a label above its heading or a subheading under it (BlockHead.tsx in blocks/, an <hgroup>; only a
1818
+ /* A block's head with a label above its heading or a subheading under it (BlockHead.tsx in blocks/, an <hgroup>; only a
1815
1819
  heading is the text's own <h2>, without this): the label in small capitals, spaced out, in the accent; the
1816
1820
  subheading larger and quiet. Mostly in the text's style (.prose), so outside the layers, like the video above, whose
1817
1821
  margins it sets in place of the text's; a theme that places a block outside the text sets its own (.block-place in
1818
1822
  the standard theme). A label above a heading in a text has the same look. A theme gives it its own look outside the layers too (the standard theme: in the middle). */
1819
- .block-head {
1820
- margin-block: 2.5em 1.25em;
1821
- }
1822
- .block-head:first-child {
1823
- margin-top: 0;
1824
- }
1825
- .block-head > h2,
1826
- .block-head > .block-eyebrow,
1827
- .block-head > .block-subheading {
1828
- margin-block: 0;
1829
- }
1830
- .block-head > * + h2,
1831
- .block-head > * + .block-subheading {
1832
- margin-top: 0.75rem;
1833
- }
1834
- .block-head > .block-eyebrow + h2 {
1835
- margin-top: 0.5rem;
1836
- }
1837
- .block-head > .block-eyebrow,
1838
- .prose .block-eyebrow {
1839
- font-size: var(--type-label-medium-size);
1840
- line-height: var(--type-label-medium-line-height);
1841
- font-weight: var(--type-label-medium-weight);
1842
- letter-spacing: 0.12em;
1843
- text-transform: uppercase;
1844
- color: var(--block-accent);
1845
- }
1846
- .block-head > .block-subheading {
1847
- font-size: var(--type-title-large-size);
1848
- line-height: var(--type-title-large-line-height);
1849
- font-weight: var(--type-title-large-weight);
1850
- letter-spacing: var(--type-title-large-tracking);
1851
- color: var(--block-muted);
1852
- }
1823
+ .block-head {
1824
+ margin-block: 2.5em 1.25em;
1825
+ }
1826
+ .block-head:first-child {
1827
+ margin-top: 0;
1828
+ }
1829
+ .block-head > h2,
1830
+ .block-head > .block-eyebrow,
1831
+ .block-head > .block-subheading {
1832
+ margin-block: 0;
1833
+ }
1834
+ .block-head > * + h2,
1835
+ .block-head > * + .block-subheading {
1836
+ margin-top: 0.75rem;
1837
+ }
1838
+ .block-head > .block-eyebrow + h2 {
1839
+ margin-top: 0.5rem;
1840
+ }
1841
+ .block-head > .block-eyebrow,
1842
+ .prose .block-eyebrow {
1843
+ font-size: var(--type-label-medium-size);
1844
+ line-height: var(--type-label-medium-line-height);
1845
+ font-weight: var(--type-label-medium-weight);
1846
+ letter-spacing: 0.12em;
1847
+ text-transform: uppercase;
1848
+ color: var(--block-accent);
1849
+ }
1850
+ .block-head > .block-subheading {
1851
+ font-size: var(--type-title-large-size);
1852
+ line-height: var(--type-title-large-line-height);
1853
+ font-weight: var(--type-title-large-weight);
1854
+ letter-spacing: var(--type-title-large-tracking);
1855
+ color: var(--block-muted);
1856
+ }
1853
1857
 
1854
- /* A label above a heading in a text (packages/editor/lib/eyebrow.mjs): a block's label, with the heading's space above
1858
+ /* A label above a heading in a text (packages/editor/lib/eyebrow.mjs): a block's label, with the heading's space above
1855
1859
  it and close to its heading. */
1856
- .prose .block-eyebrow {
1857
- margin-block: 2em 0;
1858
- }
1859
- .prose .block-eyebrow + :is(h1, h2, h3) {
1860
- margin-top: 0.5rem;
1861
- }
1860
+ .prose .block-eyebrow {
1861
+ margin-block: 2em 0;
1862
+ }
1863
+ .prose .block-eyebrow + :is(h1, h2, h3) {
1864
+ margin-top: 0.5rem;
1865
+ }
1862
1866
 
1863
- /* A word or more in one of the site's colours, in a text (packages/editor/lib/text-colors.mjs, the colour button in the
1867
+ /* A word or more in one of the site's colours, in a text (packages/editor/lib/text-colors.mjs, the colour button in the
1864
1868
  editor): the accent, or the quiet text's, both readable on the background, the surfaces and the tint (the site's
1865
1869
  pairs of colours, checked under "Farver"). Bold, italic and a link in it have its colour. Outside the layers, as it
1866
1870
  is in the text's style (.prose), whose colours of bold text and links it goes over. On a picture behind a hero's
1867
1871
  text, where only the text's colour can be read, it is the text's. */
1868
- .color-accent,
1869
- .color-accent :is(strong, em, a) {
1870
- color: var(--block-accent);
1871
- }
1872
- .color-muted,
1873
- .color-muted :is(strong, em, a) {
1874
- color: var(--block-muted);
1875
- }
1876
- .block-hero-behind :is(.color-accent, .color-muted),
1877
- .block-hero-behind :is(.color-accent, .color-muted) :is(strong, em, a) {
1878
- color: inherit;
1879
- }
1872
+ .color-accent,
1873
+ .color-accent :is(strong, em, a) {
1874
+ color: var(--block-accent);
1875
+ }
1876
+ .color-muted,
1877
+ .color-muted :is(strong, em, a) {
1878
+ color: var(--block-muted);
1879
+ }
1880
+ .block-hero-behind :is(.color-accent, .color-muted),
1881
+ .block-hero-behind :is(.color-accent, .color-muted) :is(strong, em, a) {
1882
+ color: inherit;
1883
+ }
1880
1884
 
1881
- /* A word in the accent's or the quiet text's colour (above) on a section with the accent as its background would not
1885
+ /* A word in the accent's or the quiet text's colour (above) on a section with the accent as its background would not
1882
1886
  be read: there it has the section's text colour. */
1883
- .text-section-accent :is(.color-accent, .color-muted),
1884
- .text-section-accent :is(.color-accent, .color-muted) :is(strong, em, a) {
1885
- color: inherit;
1886
- }
1887
+ .text-section-accent :is(.color-accent, .color-muted),
1888
+ .text-section-accent :is(.color-accent, .color-muted) :is(strong, em, a) {
1889
+ color: inherit;
1890
+ }
1887
1891
 
1888
- /* The first and last parts of a coloured section lose the prose margins that would add to the panel's padding. Outside
1892
+ /* The first and last parts of a coloured section lose the prose margins that would add to the panel's padding. Outside
1889
1893
  the layers, as the prose style (Tailwind's typography) is in the utilities layer, which wins over components. */
1890
- .text-section > :first-child {
1891
- margin-top: 0;
1892
- }
1893
- .text-section > :last-child {
1894
- margin-bottom: 0;
1894
+ .text-section > :first-child {
1895
+ margin-top: 0;
1896
+ }
1897
+ .text-section > :last-child {
1898
+ margin-bottom: 0;
1899
+ }
1895
1900
  }
@@ -1,4 +1,7 @@
1
1
  /*
2
+ Material is every theme's fallback: its rules that set more than custom properties sit in @layer fallback (declared last in
3
+ base.css), so a theme's own unlayered rules win over them, whatever the specificity. Custom properties stay unlayered.
4
+
2
5
  The theme's variables: what the standard theme's own components (components/, layouts/, templates/) read, so
3
6
  another theme changes them in its styles/global.css instead of with selectors on their markup (docs/editor.md,
4
7
  "Temaets variabler"). The defaults give the standard theme's look, from the theme's own --radius, --shadow and colours.
@@ -172,11 +175,13 @@
172
175
  }
173
176
  }
174
177
 
175
- /* A picture that opens the text (a paragraph of its own) starts where a paragraph would: its space above would come on
178
+ @layer fallback {
179
+ /* A picture that opens the text (a paragraph of its own) starts where a paragraph would: its space above would come on
176
180
  top of the space under the page's heading. For every theme, also one with its own text style; outside the
177
181
  layers, like the pictures' own space in the text. */
178
- .prose > p:first-child > :where(img, .picture-crop):only-child {
179
- margin-top: 0;
182
+ .prose > p:first-child > :where(img, .picture-crop):only-child {
183
+ margin-top: 0;
184
+ }
180
185
  }
181
186
 
182
187
  /* Going from one page of the site to another, the old page fades into the new (a cross-document view transition,
@@ -186,44 +191,48 @@
186
191
  @view-transition {
187
192
  navigation: auto;
188
193
  }
189
- ::view-transition-old(root),
190
- ::view-transition-new(root) {
191
- animation-duration: var(--theme-page-fade);
192
- animation-timing-function: var(--ease);
193
- }
194
194
  }
195
+ @layer fallback {
196
+ @media (prefers-reduced-motion: no-preference) {
197
+ ::view-transition-old(root),
198
+ ::view-transition-new(root) {
199
+ animation-duration: var(--theme-page-fade);
200
+ animation-timing-function: var(--ease);
201
+ }
202
+ }
195
203
 
196
- /* A page with a block beside its text (PageLayout.astro): the text, then the column (on a phone below the text); from medium
204
+ /* A page with a block beside its text (PageLayout.astro): the text, then the column (on a phone below the text); from medium
197
205
  two columns, the second one staying in view while the text is read. */
198
- .page-with-side > .page-side {
199
- margin-top: 3rem;
200
- }
201
- @media (width >= theme(--breakpoint-medium)) {
202
- .page-with-side {
203
- display: grid;
204
- grid-template-columns: minmax(0, 1fr) var(--theme-side-width);
205
- column-gap: var(--theme-side-gap);
206
- align-items: start;
207
- }
208
206
  .page-with-side > .page-side {
209
- margin-top: 0;
210
- position: sticky;
211
- top: 6rem;
207
+ margin-top: 3rem;
208
+ }
209
+ @media (width >= theme(--breakpoint-medium)) {
210
+ .page-with-side {
211
+ display: grid;
212
+ grid-template-columns: minmax(0, 1fr) var(--theme-side-width);
213
+ column-gap: var(--theme-side-gap);
214
+ align-items: start;
215
+ }
216
+ .page-with-side > .page-side {
217
+ margin-top: 0;
218
+ position: sticky;
219
+ top: 6rem;
220
+ }
212
221
  }
213
- }
214
222
 
215
- /* A page's and a post's title (PageHeader.astro, PageLayout.astro, PostLayout.astro, Home.astro): the display role, by
223
+ /* A page's and a post's title (PageHeader.astro, PageLayout.astro, PostLayout.astro, Home.astro): the display role, by
216
224
  the variables above. */
217
- .theme-title {
218
- font-size: var(--theme-title-size);
219
- line-height: var(--theme-title-leading);
220
- font-weight: var(--type-display-small-weight);
221
- letter-spacing: var(--type-display-small-tracking);
222
- }
223
- @media (width >= theme(--breakpoint-medium)) {
224
225
  .theme-title {
225
- font-size: var(--theme-title-size-large);
226
- font-weight: var(--type-display-medium-weight);
227
- letter-spacing: var(--type-display-medium-tracking);
226
+ font-size: var(--theme-title-size);
227
+ line-height: var(--theme-title-leading);
228
+ font-weight: var(--type-display-small-weight);
229
+ letter-spacing: var(--type-display-small-tracking);
230
+ }
231
+ @media (width >= theme(--breakpoint-medium)) {
232
+ .theme-title {
233
+ font-size: var(--theme-title-size-large);
234
+ font-weight: var(--type-display-medium-weight);
235
+ letter-spacing: var(--type-display-medium-tracking);
236
+ }
228
237
  }
229
238
  }