@keenmate/pure-css 1.0.0-rc03 → 1.0.0-rc05

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.
@@ -0,0 +1,76 @@
1
+ /* ========================================
2
+ Fit — Floating-menu sink (fit.js)
3
+ The self-contained relocation target for
4
+ data-pc-fit="relocate" data-pc-fit-target="floating-menu".
5
+ fit.js creates the trigger (pinned in the container) + panel (on <body>)
6
+ on demand and toggles --open; this styles them. No sidebar dependency.
7
+ Relocated from pure-admin-core: the fit engine (fit.js) + its flyout sink
8
+ now live in the foundation package.
9
+ ======================================== */
10
+ @use 'variables/index' as *;
11
+
12
+ // Trigger — a compact "•••" button fit pins at the end of the container. Hidden
13
+ // (inline style) by the engine until at least one slot lives in the panel.
14
+ .pc-fit-flyout__trigger {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ flex: 0 0 auto;
19
+ width: 2.8rem;
20
+ height: 2.8rem;
21
+ padding: 0;
22
+ border: $border-width-base solid transparent;
23
+ border-radius: var(--pc-border-radius);
24
+ background: transparent;
25
+ color: inherit;
26
+ cursor: pointer;
27
+
28
+ &:hover { background: var(--pc-hover-bg, rgba(0, 0, 0, 0.05)); }
29
+ &:focus-visible { outline: 2px solid var(--pc-accent-color); outline-offset: 1px; }
30
+ &[aria-expanded='true'] { background: var(--pc-hover-bg, rgba(0, 0, 0, 0.08)); }
31
+ }
32
+
33
+ // The three dots, drawn so the sink needs no icon-font dependency.
34
+ .pc-fit-flyout__dots {
35
+ width: 0.4rem;
36
+ height: 0.4rem;
37
+ border-radius: 50%;
38
+ background: currentColor;
39
+ position: relative;
40
+
41
+ &::before,
42
+ &::after {
43
+ content: '';
44
+ position: absolute;
45
+ top: 0;
46
+ width: 0.4rem;
47
+ height: 0.4rem;
48
+ border-radius: 50%;
49
+ background: currentColor;
50
+ }
51
+ &::before { inset-inline-start: -0.7rem; }
52
+ &::after { inset-inline-start: 0.7rem; }
53
+ }
54
+
55
+ // Panel — appended to <body>, positioned by JS (fixed, near the trigger).
56
+ .pc-fit-flyout__panel {
57
+ position: fixed;
58
+ z-index: $z-index-navbar + 10;
59
+ display: none;
60
+ min-width: 20rem;
61
+ max-width: 32rem;
62
+ padding: $spacing-sm;
63
+ background: var(--pc-card-bg);
64
+ border: $border-width-base solid var(--pc-border-color);
65
+ border-radius: var(--pc-border-radius);
66
+ box-shadow: $shadow-lg;
67
+
68
+ &--open { display: block; }
69
+ }
70
+
71
+ // Each relocated slot is wrapped in an item row inside the panel.
72
+ .pc-fit-flyout__item {
73
+ padding: $spacing-xs $spacing-sm;
74
+
75
+ & + & { border-top: $border-width-base solid var(--pc-border-color); }
76
+ }
@@ -1,37 +1,37 @@
1
- /* Pure Admin Visual Framework - Default Font Definitions */
2
-
3
- // Default framework fonts - themes can override these
4
- // Note: Font size classes for <html> are in _utilities.scss (use px values for correct rem scaling)
5
-
6
- // Font family classes with system defaults
7
- .font-family-system {
8
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
9
-
10
- *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
11
- font-family: inherit;
12
- }
13
- }
14
-
15
- .font-family-sans {
16
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
17
-
18
- *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
19
- font-family: inherit;
20
- }
21
- }
22
-
23
- .font-family-serif {
24
- font-family: Georgia, "Times New Roman", Times, serif;
25
-
26
- *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
27
- font-family: inherit;
28
- }
29
- }
30
-
31
- .font-family-mono {
32
- font-family: "Courier New", Courier, monospace;
33
-
34
- *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
35
- font-family: inherit;
36
- }
1
+ /* Pure Admin Visual Framework - Default Font Definitions */
2
+
3
+ // Default framework fonts - themes can override these
4
+ // Note: Font size classes for <html> are in _utilities.scss (use px values for correct rem scaling)
5
+
6
+ // Font family classes with system defaults
7
+ .font-family-system {
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
9
+
10
+ *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
11
+ font-family: inherit;
12
+ }
13
+ }
14
+
15
+ .font-family-sans {
16
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
17
+
18
+ *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
19
+ font-family: inherit;
20
+ }
21
+ }
22
+
23
+ .font-family-serif {
24
+ font-family: Georgia, "Times New Roman", Times, serif;
25
+
26
+ *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
27
+ font-family: inherit;
28
+ }
29
+ }
30
+
31
+ .font-family-mono {
32
+ font-family: "Courier New", Courier, monospace;
33
+
34
+ *, h1, h2, h3, h4, h5, h6, p, div, span, button, input, textarea, select {
35
+ font-family: inherit;
36
+ }
37
37
  }
@@ -0,0 +1,168 @@
1
+ @use 'variables/index' as *;
2
+ /* ========================================
3
+ Layout Container
4
+ Main layout wrapper, content areas, sticky/scroll modes
5
+ ======================================== */
6
+
7
+ // Layout wrapper - applied to body for mode-specific classes
8
+ body.pc-layout--sticky {
9
+ height: 100vh;
10
+ overflow: hidden;
11
+ }
12
+
13
+ // Layout Container - centered, starts below navbar
14
+ .pc-layout {
15
+ max-width: 100%;
16
+ margin: $navbar-height auto 0;
17
+ background-color: var(--pc-main-bg);
18
+ overflow: visible; // Allow tooltips to escape
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+
23
+ // Constrained widths - controlled by body class
24
+ body.pc-container-sm .pc-layout {
25
+ max-width: $layout-container-sm;
26
+ box-shadow: $shadow-2xl;
27
+ border-left: $border-width-base solid var(--pc-border-color);
28
+ border-right: $border-width-base solid var(--pc-border-color);
29
+ }
30
+
31
+ body.pc-container-md .pc-layout {
32
+ max-width: $layout-container-md;
33
+ box-shadow: $shadow-2xl;
34
+ border-left: $border-width-base solid var(--pc-border-color);
35
+ border-right: $border-width-base solid var(--pc-border-color);
36
+ }
37
+
38
+ body.pc-container-lg .pc-layout {
39
+ max-width: $layout-container-lg;
40
+ box-shadow: $shadow-2xl;
41
+ border-left: $border-width-base solid var(--pc-border-color);
42
+ border-right: $border-width-base solid var(--pc-border-color);
43
+ }
44
+
45
+ body.pc-container-xl .pc-layout {
46
+ max-width: $layout-container-xl;
47
+ box-shadow: $shadow-xl;
48
+ border-left: $border-width-base solid var(--pc-border-color);
49
+ border-right: $border-width-base solid var(--pc-border-color);
50
+ }
51
+
52
+ body.pc-container-2xl .pc-layout {
53
+ max-width: $layout-container-2xl;
54
+ box-shadow: $shadow-xl;
55
+ border-left: $border-width-base solid var(--pc-border-color);
56
+ border-right: $border-width-base solid var(--pc-border-color);
57
+ }
58
+
59
+ // Sticky mode - fixed height
60
+ body.pc-layout--sticky .pc-layout {
61
+ height: calc(100vh - #{$navbar-height}); // Full viewport minus top margin
62
+ }
63
+
64
+ // Scroll mode - minimum height
65
+ body:not(.pc-layout--sticky) .pc-layout {
66
+ min-height: calc(100vh - #{$navbar-height}); // Ensure footer reaches bottom
67
+ }
68
+
69
+ // Layout inner container - flex wrapper for sidebar + content
70
+ .pc-layout__inner {
71
+ display: flex;
72
+ width: 100%;
73
+ flex: 1; // Take available space, pushing footer to bottom
74
+ }
75
+
76
+ // Sticky mode - inner container takes full height minus footer
77
+ body.pc-layout--sticky .pc-layout__inner {
78
+ flex: 1;
79
+ overflow: hidden; // Prevent overflow, let content wrapper scroll
80
+ }
81
+
82
+ // Content wrapper - wraps main content only
83
+ .pc-layout__content {
84
+ flex: 1;
85
+ display: flex;
86
+ flex-direction: column;
87
+ overflow-y: auto; // Always scrollable
88
+ }
89
+
90
+ // Main content area
91
+ .pc-layout__main {
92
+ flex: 1;
93
+ padding: $spacing-base;
94
+ container-type: inline-size;
95
+ }
96
+
97
+ // Footer - outside inner, sibling to inner
98
+ .pc-layout__footer {
99
+ min-height: $footer-height;
100
+ background-color: var(--pc-footer-bg);
101
+ border-top: $border-width-base solid var(--pc-footer-border-color);
102
+ display: flex;
103
+ align-items: center;
104
+ padding: $spacing-xs $spacing-base;
105
+ flex-shrink: 0; // Don't shrink footer
106
+ gap: $spacing-base;
107
+ }
108
+
109
+ // Footer sections - Start/Center/End layout (mirrors navbar structure)
110
+ .pc-footer__start {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: $spacing-base;
114
+ flex-shrink: 0;
115
+ }
116
+
117
+ .pc-footer__center {
118
+ flex: 1;
119
+ min-width: 0; // Allow shrinking for ellipsis
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ }
124
+
125
+ .pc-footer__end {
126
+ display: flex;
127
+ align-items: center;
128
+ gap: $spacing-base;
129
+ flex-shrink: 0;
130
+
131
+ &--vertical {
132
+ flex-direction: column;
133
+ align-items: flex-end;
134
+ gap: $spacing-xs;
135
+ }
136
+ }
137
+
138
+ // Responsive footer - stack sections on mobile
139
+ @media (max-width: $mobile-breakpoint) {
140
+ .pc-layout__footer {
141
+ flex-wrap: wrap;
142
+ min-height: auto;
143
+ padding: $spacing-sm $spacing-base;
144
+ }
145
+
146
+ .pc-footer__start,
147
+ .pc-footer__center,
148
+ .pc-footer__end {
149
+ flex-shrink: 1;
150
+ min-width: 0;
151
+ }
152
+
153
+ .pc-footer__start {
154
+ flex: 1 1 auto;
155
+ }
156
+
157
+ .pc-footer__center {
158
+ display: none; // Hide empty center on mobile
159
+ }
160
+
161
+ .pc-footer__end {
162
+ flex: 0 1 auto;
163
+
164
+ &--vertical {
165
+ align-items: flex-end;
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,181 @@
1
+ @use 'variables/index' as *;
2
+ /* ========================================
3
+ Layout Responsive
4
+ Mobile and tablet media queries
5
+ ======================================== */
6
+
7
+ // Config CSS variables — the JS-readable single source for values that also
8
+ // exist as SCSS tokens (window.pureAdmin.config mirrors these; see
9
+ // docs/config-shared-ui-baseline.md). Non-themeable structural constants, so
10
+ // they're emitted here (not via a theme mixin) yet ship in both main.css and
11
+ // every theme. The breakpoint is a clean px value (parseFloat-friendly); the
12
+ // motion tokens are calc() expressions, so JS resolves them off a probe element
13
+ // (getComputedStyle can't resolve calc in an unregistered custom property).
14
+ // These also give CSS consumers named motion tokens as runtime vars.
15
+ :root {
16
+ --pc-mobile-breakpoint: #{$mobile-breakpoint};
17
+ --pc-transition-fast: #{$transition-fast};
18
+ --pc-transition-normal: #{$transition-normal};
19
+ --pc-transition-medium: #{$transition-medium};
20
+ --pc-transition-slow: #{$transition-slow};
21
+ --pc-easing-snappy: #{$easing-snappy};
22
+ }
23
+
24
+ // Responsive Design - Mobile and Tablet
25
+ @media (max-width: $mobile-breakpoint) {
26
+ // Mobile: the sidebar is an off-canvas drawer that SLIDES in/out, in sync with
27
+ // the profile panel (same $transition-medium / $easing-snappy). It's always
28
+ // position:fixed at the drawer size and translated off-canvas at the
29
+ // inline-start edge; `.sidebar-visible` slides it to translateX(0). This
30
+ // replaces the old snap between width:0 and a fixed overlay, which popped in
31
+ // with no animation. Content takes full width in both states because the
32
+ // drawer is out of flow (fixed).
33
+ .pc-layout__sidebar,
34
+ .pc-layout__sidebar--icon-collapse {
35
+ position: fixed;
36
+ top: $navbar-height;
37
+ inset-inline-start: 0; // RTL: flips to right
38
+ bottom: 0;
39
+ width: $mobile-sidebar-width;
40
+ z-index: 1050;
41
+ background-color: var(--pc-sidebar-bg);
42
+ opacity: 1;
43
+ overflow-y: auto;
44
+ overscroll-behavior: contain; // drawer's own scroll doesn't chain to the page
45
+ border-inline-end: none; // RTL: flips to left
46
+ transform: translateX(-100%); // off-canvas (slid out past the inline-start edge)
47
+ visibility: hidden; // inert + unfocusable while closed; flips visible on open
48
+
49
+ // Gate the transition on body.loaded like the sidebar links, so the drawer
50
+ // doesn't animate from off-canvas on first paint.
51
+ body.loaded & {
52
+ transition: transform $transition-medium $easing-snappy,
53
+ visibility $transition-medium $easing-snappy;
54
+ }
55
+ }
56
+
57
+ // RTL: the drawer sits at the inline-start (right) edge, so it slides off to
58
+ // the right when closed.
59
+ [dir="rtl"] .pc-layout__sidebar,
60
+ [dir="rtl"] .pc-layout__sidebar--icon-collapse {
61
+ transform: translateX(100%);
62
+ }
63
+
64
+ // icon-collapse mode is a full overlay drawer on mobile — force labels visible.
65
+ .pc-layout__sidebar--icon-collapse .pc-sidebar__label {
66
+ opacity: 1 !important;
67
+ width: auto !important;
68
+ overflow: visible !important;
69
+ position: static !important;
70
+ }
71
+
72
+ // Backdrop scrim: an always-present fixed pseudo that FADES its opacity, so it
73
+ // eases in/out with the drawer (a pseudo that only existed while open couldn't
74
+ // fade out). Inert + invisible when closed. On body::before so it's positioned
75
+ // against the viewport (not a transformed ancestor) — the toggle class lives
76
+ // on body too.
77
+ body::before {
78
+ content: "";
79
+ position: fixed;
80
+ top: $navbar-height;
81
+ inset-inline: 0; // RTL: covers full width
82
+ bottom: 0;
83
+ background-color: $mobile-backdrop-bg;
84
+ z-index: 1040;
85
+ opacity: 0;
86
+ visibility: hidden;
87
+ pointer-events: none;
88
+
89
+ body.loaded & {
90
+ transition: opacity $transition-medium $easing-snappy,
91
+ visibility $transition-medium $easing-snappy;
92
+ }
93
+ }
94
+
95
+ // Mobile: drawer open — slide it in, reveal the scrim, lock background scroll.
96
+ .sidebar-visible {
97
+ // Standard modal-drawer scroll-lock: while the drawer + scrim are open the
98
+ // page behind must not scroll (otherwise dragging the scrim strip scrolls
99
+ // the content under the fixed drawer, exposing that the drawer is only
100
+ // viewport-high). This covers desktop browsers + Android; iOS Safari ignores
101
+ // body{overflow:hidden} for touch scroll, so the mobile toggle also applies
102
+ // a position:fixed body lock in JS (demo layout.mustache). See
103
+ // docs/mobile-sidebar-overlay.md.
104
+ overflow: hidden;
105
+
106
+ .pc-layout__sidebar,
107
+ .pc-layout__sidebar--icon-collapse {
108
+ transform: translateX(0); // slide in (source order beats the RTL closed rule)
109
+ visibility: visible;
110
+ }
111
+
112
+ &::before {
113
+ opacity: 1;
114
+ visibility: visible;
115
+ pointer-events: auto; // tap the scrim to dismiss
116
+ }
117
+ }
118
+
119
+ // Mobile overlay: the sidebar is a fixed 90vw sheet and resize is disabled in
120
+ // JS at this width (sidebar-resize.js MOBILE_MAX = $mobile-breakpoint). Hide
121
+ // the handle so the coarse-pointer grip pill / edge line don't render as a
122
+ // dead affordance riding the overlay's edge (where it also straddles the
123
+ // backdrop-dismiss tap zone). Touch resize still works in the tablet band
124
+ // above this breakpoint, where the coarse-pointer affordance is kept.
125
+ .pc-sidebar-resize {
126
+ display: none;
127
+ }
128
+
129
+ // Adjust navbar for mobile
130
+ .pc-navbar__inner {
131
+ gap: $spacing-sm;
132
+ }
133
+
134
+ // Legacy navs hard-hide on mobile (navigation is expected in the sidebar).
135
+ // A nav opted into progressive collapse (data-pc-fit-nav) manages its
136
+ // own width at every breakpoint, so it must stay visible here.
137
+ .pc-navmenu:not([data-pc-fit-nav]) {
138
+ display: none;
139
+ }
140
+
141
+ // Hide theme switcher label on mobile
142
+ .theme-switcher {
143
+ label {
144
+ display: none;
145
+ }
146
+ }
147
+ }
148
+
149
+ @media (max-width: $tablet-breakpoint) and (min-width: $tablet-breakpoint-min) {
150
+ // Tablet: reduce the sidebar by default, but honour an explicit resize.
151
+ // The default (un-resized) width caps at $sidebar-width-tablet via min() so
152
+ // content gets room and the base $sidebar-width (28.8rem) doesn't dominate a
153
+ // tablet. The old `width: $sidebar-width-tablet` literal (specificity 0,1,0)
154
+ // overrode the var-based base rule outright and snapped every sidebar to 16rem.
155
+ .pc-layout__sidebar {
156
+ width: min(var(--pc-local-sidebar-width), #{$sidebar-width-tablet});
157
+
158
+ &__nav ul li a {
159
+ padding: $spacing-sm $spacing-md;
160
+ font-size: $font-size-sm;
161
+ }
162
+ }
163
+
164
+ // Once the user has actually drag-resized (sidebar-resize.js sets this class),
165
+ // honour their exact width here — WITHOUT the tablet cap. This is required for
166
+ // the resize handle to work at all in this band: the drag floor
167
+ // (--pc-local-sidebar-min-width, 18rem) is ABOVE the 16rem cap, so every width
168
+ // the drag can produce is clamped by min() and the sidebar never visibly moves
169
+ // — the handle looks dead. Gating on the explicit-resize class (not just
170
+ // --resizable) keeps the narrow tablet default until the user opts in.
171
+ //
172
+ // MUST exclude .sidebar-hidden: this selector (0,2,1) outweighs
173
+ // `.sidebar-hidden .pc-layout__sidebar { width: 0 }` (0,2,0), so once resized a
174
+ // sidebar stays full-width when the burger hides it and the content never
175
+ // reclaims the space — but ONLY in this tablet band (desktop has no such
176
+ // override). `:not(.sidebar-hidden)` yields to the hide rule while keeping the
177
+ // resized width whenever the sidebar is actually shown.
178
+ body.pc-sidebar-resized:not(.sidebar-hidden) .pc-layout__sidebar {
179
+ width: var(--pc-local-sidebar-width);
180
+ }
181
+ }