css-is-awesome 1.19.2 → 1.21.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 (41) hide show
  1. package/AGENTS.md +11 -5
  2. package/CHANGELOG.md +20 -0
  3. package/CONTRACT.md +10 -0
  4. package/MIGRATION.md +58 -0
  5. package/README.md +19 -11
  6. package/bin/analyze.cjs +47 -2
  7. package/bin/cia.cjs +10 -0
  8. package/bin/fix-theme.cjs +151 -0
  9. package/llm.txt +5 -5
  10. package/mcp/server.cjs +64 -3
  11. package/package.json +6 -3
  12. package/public/theme.css +138 -0
  13. package/public/themes/boilerplate/theme.css +6 -0
  14. package/public/themes/boilerplate-dark/theme.css +6 -0
  15. package/public/themes/boilerplate-light/theme.css +6 -0
  16. package/public/themes/cupertino/theme.css +6 -0
  17. package/public/themes/cupertino-dark/theme.css +6 -0
  18. package/public/themes/cupertino-light/theme.css +6 -0
  19. package/public/themes/glass/theme.css +6 -0
  20. package/public/themes/glass-dark/theme.css +6 -0
  21. package/public/themes/glass-light/theme.css +6 -0
  22. package/public/themes/graphite/theme.css +6 -0
  23. package/public/themes/graphite-dark/theme.css +6 -0
  24. package/public/themes/graphite-light/theme.css +6 -0
  25. package/public/themes/press/theme.css +6 -0
  26. package/public/themes/press-dark/theme.css +6 -0
  27. package/public/themes/press-light/theme.css +6 -0
  28. package/public/themes/prism/theme.css +6 -0
  29. package/public/themes/prism-dark/theme.css +6 -0
  30. package/public/themes/prism-light/theme.css +6 -0
  31. package/public/themes/sketchbook/theme.css +6 -0
  32. package/public/themes/sketchbook-dark/theme.css +6 -0
  33. package/public/themes/sketchbook-light/theme.css +6 -0
  34. package/public/themes/terminal/theme.css +6 -0
  35. package/public/themes/terminal-dark/theme.css +6 -0
  36. package/public/themes/terminal-light/theme.css +6 -0
  37. package/scripts/audit-pairs.json +171 -23
  38. package/scripts/fix-theme.cjs +184 -0
  39. package/scripts/theme-contract.json +33 -4
  40. package/scripts/theme-validator.js +40 -1
  41. package/scss/_layout.scss +129 -0
package/scss/_layout.scss CHANGED
@@ -595,3 +595,132 @@ $_flow: (
595
595
  margin-inline-start: auto;
596
596
  }
597
597
  }
598
+
599
+ // ============================================================================
600
+ // PAGE SURFACES — hero + band
601
+ // ============================================================================
602
+ // Two page-level backgrounds a theme can own outright: `hero` for a landing
603
+ // page, `band` for a section stripe or a footer. Each slot carries four
604
+ // optional tokens — background colour, image, ink, scrim.
605
+ //
606
+ // NOTHING HERE RUNS UNTIL YOU ASK FOR IT. cia never applies a page surface
607
+ // automatically: not on `body`, not from a layout mixin, not from the reset.
608
+ // That guarantee is why adding these tokens to every shipped theme cannot
609
+ // change an existing consumer's page, and `tests/no-auto-surface.test.mjs`
610
+ // asserts it against the compiled bundles so it cannot be broken by accident.
611
+ //
612
+ // Every token falls back, so a theme that declares none of them still renders:
613
+ // background → the deprecated --background-hero, then the page surface
614
+ // ink → --text-primary
615
+ // image → none (and with no image, no scrim is drawn)
616
+ //
617
+ // The scrim is layered as a gradient in the SAME background-image as the
618
+ // image rather than as a pseudo-element or an extra wrapper, so `surface`
619
+ // works identically on <body> and on a <section>, and stacking context,
620
+ // overflow and child positioning are left alone.
621
+
622
+ $_surface-slots: (hero, band) !default;
623
+
624
+ @mixin surface($slot: hero, $image: null, $scrim: null) {
625
+ @if not list.index($_surface-slots, $slot) {
626
+ @error "surface(): unknown slot '#{$slot}'. Expected one of: hero, band.";
627
+ }
628
+
629
+ $bg: var(--page-#{$slot}-bg, var(--background-#{$slot}, var(--surface-default, var(--paper))));
630
+ $ink: var(--page-#{$slot}-ink, var(--text-primary, var(--ink)));
631
+
632
+ // The scrim only paints when there is an image UNDER it. Defaulting it to a
633
+ // colour would put a 50% black wash over every colour-only hero — which is
634
+ // both wrong and a silent visual change for anyone who sets just a colour.
635
+ //
636
+ // $image given → the caller has an image, so the scrim defaults to the
637
+ // 50% black that keeps text legible over a photo.
638
+ // $image omitted → the image comes from the token (or is absent), so the
639
+ // scrim defaults to `transparent` and the token carries
640
+ // the value. The theme editor writes a scrim whenever it
641
+ // writes an image, so the safe default still arrives.
642
+ $img: var(--page-#{$slot}-image, none);
643
+ $scrim-default: transparent;
644
+ @if $image {
645
+ $img: $image;
646
+ $scrim-default: rgb(0 0 0 / 0.5);
647
+ }
648
+ $scrim-color: var(--page-#{$slot}-scrim, #{$scrim-default});
649
+ @if $scrim {
650
+ $scrim-color: $scrim;
651
+ }
652
+
653
+ background-color: #{$bg};
654
+ background-image: linear-gradient(#{$scrim-color}, #{$scrim-color}), #{$img};
655
+ background-size: cover;
656
+ background-position: center;
657
+ background-repeat: no-repeat;
658
+ color: #{$ink};
659
+ }
660
+
661
+ // The attribute form of the same thing, for consumers with no Sass build.
662
+ // Emitted only when this mixin is called — it is not part of the reset.
663
+ @mixin surface-attributes {
664
+ @each $slot in $_surface-slots {
665
+ [data-surface='#{$slot}'] {
666
+ @include surface($slot);
667
+ }
668
+ }
669
+ }
670
+
671
+ // ----------------------------------------------------------------------------
672
+ // WAVE DIVIDER — a curved edge between two sections, pure CSS
673
+ // ----------------------------------------------------------------------------
674
+ // Draws the curve with a radial-gradient mask on a ::before/::after, so there
675
+ // is no SVG file to host and the shape inherits whatever colour you give it.
676
+ // `$direction: down` curves the edge INTO the element (a valley at the top);
677
+ // `up` bulges it outward. `$flip: true` puts the wave on the bottom edge.
678
+ //
679
+ // .hero { @include cia.surface(hero); @include cia.wave-divider($height: 4rem); }
680
+ //
681
+ // The wave paints in `$color`, which defaults to the NEXT section's surface so
682
+ // the curve reads as that section rising into this one.
683
+ @mixin wave-divider(
684
+ $height: 3rem,
685
+ $color: var(--surface-default, var(--paper)),
686
+ $direction: down,
687
+ $flip: false,
688
+ $position: after
689
+ ) {
690
+ @if $direction != down and $direction != up {
691
+ @error "wave-divider(): $direction must be `down` or `up`, got `#{$direction}`.";
692
+ }
693
+ @if $position != after and $position != before {
694
+ @error "wave-divider(): $position must be `after` or `before`, got `#{$position}`.";
695
+ }
696
+
697
+ position: relative;
698
+ isolation: isolate;
699
+
700
+ &::#{$position} {
701
+ content: '';
702
+ position: absolute;
703
+ inset-inline: 0;
704
+ block-size: $height;
705
+ pointer-events: none;
706
+ z-index: 1;
707
+
708
+ @if $flip {
709
+ inset-block-start: calc(100% - #{$height});
710
+ } @else {
711
+ inset-block-end: calc(100% - #{$height});
712
+ }
713
+
714
+ // An ellipse wider than the box gives a shallow, natural curve rather
715
+ // than the circular arc a border-radius would produce.
716
+ @if $direction == down {
717
+ background: #{$color};
718
+ // stylelint-disable-next-line function-no-unknown
719
+ mask: radial-gradient(120% #{$height} at 50% 0%, transparent 99%, #000 100%);
720
+ } @else {
721
+ background: #{$color};
722
+ // stylelint-disable-next-line function-no-unknown
723
+ mask: radial-gradient(120% #{$height} at 50% 100%, #000 99%, transparent 100%);
724
+ }
725
+ }
726
+ }