@atelic-action/ui 0.17.1 → 0.17.2

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.
package/README.md CHANGED
@@ -248,7 +248,7 @@ export function ArtifactShell(props: Omit<ArtifactShellProps, "currentPath">) {
248
248
  }
249
249
  ```
250
250
 
251
- **The note** is the first touch for a business whose site publishes no email address, served at `/notes` and wrapped in `ArtifactShell` exactly as the writeup is. It is the writeup's sibling, not a variant: a title and a dateline, the opener, two lanes of findings (what the customer sees, what sits under the hood), the steps the work would start with, and the close over the shared `SignOff`. Every prop is plain data, so a routine can write a note as JSON; a paragraph is a string or an array of strings and `{ text, href }` links, and the sign off person's `role` is the one field that also takes a node. A finding is a `Callout` with its `WriteupShot` under it. It reads on a phone first, stacks the two lanes as separate sections top to bottom at every width and on paper, never side by side, and prints to one letter sheet under the writeup's `@page`.
251
+ **The note** is the first touch for a business whose site publishes no email address, served at `/notes` and wrapped in `ArtifactShell` exactly as the writeup is. It is the writeup's sibling, not a variant: a title and a dateline, the opener, two lanes of findings (what the customer sees, what sits under the hood), the steps the work would start with, and the close over the shared `SignOff`. Every prop is plain data, so a routine can write a note as JSON; a paragraph is a string or an array of strings and `{ text, href }` links, and the sign off person's `role` is the one field that also takes a node. A finding is a `Callout` with its `WriteupShot` under it. Its column is the writeup's 920px, centered under the header with the header's `--gutter`, and its text runs the column's full width. A finding sets its screen under its words on a phone and beside them, on the right, when wide and on paper. It reads on a phone first, stacks the two lanes as separate sections top to bottom at every width and on paper, never side by side, and prints to one letter sheet under the writeup's `@page`.
252
252
 
253
253
  The documents import no stylesheet of their own. A site imports `@atelic-action/ui/styles/artifact.css` in its base sheet after `gate.css` and before its `theme.css`, so every page carries the rules whichever route it is. The sheet holds two `@page` rules, the monthly page's and then the writeup's, and the later one wins for every page a site prints; the order inside the sheet is the order the template's build bundled them in and is load bearing.
254
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelic-action/ui",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "Shared UI for the Atelic templates: site chrome, base styles, the component library, and the access gate they install",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -121,7 +121,7 @@ function Paragraph({ paragraph, className }: { paragraph: NoteParagraph; classNa
121
121
 
122
122
  function Finding({ finding }: { finding: NoteFinding }) {
123
123
  return (
124
- <li className="nt-finding">
124
+ <li className={finding.shot ? "nt-finding has-shot" : "nt-finding"}>
125
125
  <Callout
126
126
  tone="warn"
127
127
  title={
@@ -1,3 +1,4 @@
1
+ import { Maximize2 } from "lucide-react";
1
2
  import { newTabProps } from "../lib/newTabProps";
2
3
 
3
4
  export interface WriteupShot {
@@ -12,14 +13,19 @@ export interface WriteupShot {
12
13
  /**
13
14
  * The owner's own screen: the crop, opening the full screenshot in a new tab
14
15
  * when it has one, over its caption. The writeup and the note both wear it;
15
- * its styles (.op-shot) are the writeup's, and the note only sizes it.
16
+ * its styles (.op-shot) are the writeup's, and the note only sizes it. A
17
+ * linked crop says it opens: a hover title, and an expand badge the note
18
+ * shows on its thumbnails and the writeup keeps hidden.
16
19
  */
17
20
  export function Shot({ shot }: { shot: WriteupShot }) {
18
21
  return (
19
22
  <figure className="op-shot">
20
23
  {shot.href ? (
21
- <a href={shot.href} {...newTabProps(shot.href, true)}>
24
+ <a href={shot.href} title="Open the full picture" {...newTabProps(shot.href, true)}>
22
25
  <img src={shot.src} alt={shot.alt} loading="lazy" />
26
+ <span className="op-shot-open" aria-hidden="true">
27
+ <Maximize2 size={12} />
28
+ </span>
23
29
  </a>
24
30
  ) : (
25
31
  <span className="op-shot-frame">
@@ -1392,6 +1392,10 @@
1392
1392
  :is(.mkt, .atelic-ui) .op-shot a {
1393
1393
  cursor: zoom-in;
1394
1394
  }
1395
+ /* The expand badge on a linked crop: off here, the note turns it on. */
1396
+ :is(.mkt, .atelic-ui) .op-shot-open {
1397
+ display: none;
1398
+ }
1395
1399
  :is(.mkt, .atelic-ui) .op-shot img {
1396
1400
  width: 100%;
1397
1401
  height: auto;
@@ -1863,12 +1867,18 @@
1863
1867
 
1864
1868
  /* =================================================================
1865
1869
  THE NOTE: the first touch set as a page (Note.tsx). A letter, not a
1866
- report: one narrow column of running text, read on a phone first.
1870
+ report, read on a phone first. Its column is the writeup's 920px,
1871
+ centered under the header with the header's own gutters, and every
1872
+ line of text runs the column's full width.
1867
1873
 
1868
- - Phone first, and the one layout: the two lanes are separate
1869
- sections stacked top to bottom at every width and on paper, never
1870
- side by side, each finding a Callout with the owner's screen
1871
- under it. Wide (721px and up) only gives the page more room.
1874
+ - The two lanes are separate sections stacked top to bottom at
1875
+ every width and on paper, never side by side.
1876
+ - Phone first: a finding is its Callout with the owner's screen
1877
+ under it. Wide (721px and up) and on paper, a finding with a
1878
+ screen is one row, the words left and the screen right, top
1879
+ aligned; the screen's column sizes to the picture, a phone crop
1880
+ about 260px and a wide card up to 45% of the row. A finding
1881
+ with no screen takes the full width.
1872
1882
  - Print: one letter sheet. The chrome steps aside (the writeup's print
1873
1883
  rules already hide the header and the credit band on any artifact
1874
1884
  page), the type comes down, and the screens shrink. The note sets
@@ -1888,10 +1898,12 @@
1888
1898
  color: var(--ink);
1889
1899
  background: var(--surface);
1890
1900
  }
1901
+ /* The writeup's column (.op-doc, 920px), centered under the header and
1902
+ held off the edges by the header's gutters (chrome.css, .nav-inner). */
1891
1903
  :is(.mkt, .atelic-ui) .nt-doc {
1892
- max-width: 760px;
1904
+ max-width: calc(920px + 2 * var(--gutter));
1893
1905
  margin: 0 auto;
1894
- padding: calc(var(--nav-height) + 22px) 18px 48px;
1906
+ padding: calc(var(--nav-height) + 22px) var(--gutter) 48px;
1895
1907
  font-size: 17px;
1896
1908
  line-height: 1.6;
1897
1909
  print-color-adjust: exact;
@@ -1975,9 +1987,9 @@
1975
1987
  font-size: 16px;
1976
1988
  line-height: 1.5;
1977
1989
  }
1978
- /* The screen sits under its finding, set in from the icon so it lines up
1979
- with the words. It keeps its own shape, never wider than the lane and
1980
- never so tall it carries the page. */
1990
+ /* The screen is a thumbnail under its finding, set in from the icon so it
1991
+ lines up with the words. It keeps its own shape, held to 200px tall and
1992
+ never the lane's full width; a click opens the full picture. */
1981
1993
  :is(.mkt, .atelic-ui) .nt-finding .op-shot {
1982
1994
  margin: 10px 0 0 28px;
1983
1995
  }
@@ -1988,10 +2000,45 @@
1988
2000
  }
1989
2001
  :is(.mkt, .atelic-ui) .nt-finding .op-shot img {
1990
2002
  width: auto;
1991
- max-width: 100%;
1992
- max-height: 340px;
2003
+ max-width: min(100%, 280px);
2004
+ max-height: 200px;
2005
+ }
2006
+ /* A linked thumbnail says it opens: the badge in its corner, and the
2007
+ frame takes the accent on hover and focus. */
2008
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot a {
2009
+ position: relative;
2010
+ color: inherit;
2011
+ text-decoration: none;
2012
+ transition:
2013
+ border-color 0.15s ease,
2014
+ box-shadow 0.15s ease;
2015
+ }
2016
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot a:hover,
2017
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot a:focus-visible {
2018
+ border-color: var(--primary);
2019
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 25%, transparent);
2020
+ }
2021
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot-open {
2022
+ position: absolute;
2023
+ right: 4px;
2024
+ bottom: 4px;
2025
+ display: flex;
2026
+ align-items: center;
2027
+ justify-content: center;
2028
+ width: 20px;
2029
+ height: 20px;
2030
+ border-radius: 50%;
2031
+ background: color-mix(in srgb, var(--ink) 72%, transparent);
2032
+ color: var(--surface);
2033
+ }
2034
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot a:hover .op-shot-open,
2035
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot a:focus-visible .op-shot-open {
2036
+ background: var(--primary);
1993
2037
  }
2038
+ /* The caption wraps to the picture's width and never sets the column's. */
1994
2039
  :is(.mkt, .atelic-ui) .nt-finding .op-shot figcaption {
2040
+ width: 0;
2041
+ min-width: 100%;
1995
2042
  font-size: 12.5px;
1996
2043
  line-height: 1.45;
1997
2044
  color: var(--neutral-500);
@@ -2059,20 +2106,49 @@
2059
2106
  text-decoration: none;
2060
2107
  }
2061
2108
 
2062
- /* ---- wide: the same stacked page with room ---- */
2109
+ /* ---- wide: more room, and each screen beside its finding ---- */
2063
2110
  @media (min-width: 721px) {
2064
2111
  :is(.mkt, .atelic-ui) .nt-doc {
2065
- padding: calc(var(--nav-height) + 36px) 24px 64px;
2112
+ padding-top: calc(var(--nav-height) + 36px);
2113
+ padding-bottom: 64px;
2066
2114
  }
2067
2115
  :is(.mkt, .atelic-ui) .nt-lanes {
2068
2116
  gap: 32px;
2069
2117
  }
2070
2118
  }
2071
2119
 
2072
- /* ---- print: one letter sheet ---- */
2120
+ /* A finding with a screen is one row: the words left, the screen right,
2121
+ top aligned. The screen is a thumbnail about as tall as a three line
2122
+ finding, so a phone crop reads as a tall one about 80px wide and a
2123
+ wide card as a short wide one; the column shrinks to it, held to 45%
2124
+ of the row, with room enough for the caption under it. */
2125
+ @media print, (min-width: 721px) {
2126
+ :is(.mkt, .atelic-ui) .nt-finding.has-shot {
2127
+ display: grid;
2128
+ grid-template-columns: minmax(0, 1fr) fit-content(45%);
2129
+ column-gap: 32px;
2130
+ align-items: start;
2131
+ }
2132
+ :is(.mkt, .atelic-ui) .nt-finding.has-shot .op-shot {
2133
+ margin: 0;
2134
+ min-width: 150px;
2135
+ }
2136
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot img {
2137
+ max-width: 100%;
2138
+ max-height: 170px;
2139
+ }
2140
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot figcaption {
2141
+ font-size: 12px;
2142
+ }
2143
+ }
2144
+
2145
+ /* ---- print: one letter sheet ----
2146
+ The rows hold on paper, since they are shorter than the stacked page.
2147
+ The sheet is the page at the writeup's three quarters, the thumbnails
2148
+ are held short with room for their captions, and the badge drops. */
2073
2149
  @media print {
2074
2150
  html:has(:is(.mkt, .atelic-ui) .nt-doc) {
2075
- zoom: 0.8;
2151
+ zoom: 0.74;
2076
2152
  }
2077
2153
  :is(.mkt, .atelic-ui) .nt {
2078
2154
  background: none;
@@ -2081,26 +2157,63 @@
2081
2157
  max-width: none;
2082
2158
  padding: 0;
2083
2159
  font-size: 15px;
2084
- line-height: 1.5;
2160
+ line-height: 1.45;
2161
+ }
2162
+ :is(.mkt, .atelic-ui) .nt h1 {
2163
+ font-size: 32px;
2085
2164
  }
2086
2165
  :is(.mkt, .atelic-ui) .nt-opener {
2166
+ margin-top: 12px;
2087
2167
  font-size: 16px;
2088
2168
  }
2089
2169
  :is(.mkt, .atelic-ui) .nt-lanes,
2090
2170
  :is(.mkt, .atelic-ui) .nt-start,
2091
2171
  :is(.mkt, .atelic-ui) .nt-close {
2092
- margin-top: 20px;
2093
- padding-top: 16px;
2172
+ margin-top: 16px;
2173
+ padding-top: 14px;
2174
+ }
2175
+ :is(.mkt, .atelic-ui) .nt-lanes {
2176
+ gap: 18px;
2177
+ }
2178
+ :is(.mkt, .atelic-ui) .nt-lane h2 {
2179
+ margin-bottom: 10px;
2180
+ break-after: avoid;
2181
+ }
2182
+ :is(.mkt, .atelic-ui) .nt-findings {
2183
+ gap: 14px;
2094
2184
  }
2095
2185
  :is(.mkt, .atelic-ui) .nt-finding .callout {
2096
2186
  font-size: 14.5px;
2097
2187
  }
2188
+ :is(.mkt, .atelic-ui) .nt-finding.has-shot {
2189
+ grid-template-columns: minmax(0, 1fr) fit-content(40%);
2190
+ column-gap: 24px;
2191
+ }
2192
+ :is(.mkt, .atelic-ui) .nt-finding.has-shot .op-shot {
2193
+ min-width: 1.6in;
2194
+ }
2098
2195
  :is(.mkt, .atelic-ui) .nt-finding .op-shot img {
2099
- max-height: 1.9in;
2196
+ max-height: 1.5in;
2197
+ }
2198
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot-open {
2199
+ display: none;
2200
+ }
2201
+ :is(.mkt, .atelic-ui) .nt-finding .op-shot figcaption {
2202
+ font-size: 11.5px;
2203
+ line-height: 1.35;
2100
2204
  }
2101
2205
  :is(.mkt, .atelic-ui) .nt-finding .op-shot a {
2102
2206
  cursor: auto;
2103
2207
  }
2208
+ :is(.mkt, .atelic-ui) .nt-start ul {
2209
+ gap: 4px;
2210
+ }
2211
+ :is(.mkt, .atelic-ui) .nt-close p {
2212
+ margin-bottom: 10px;
2213
+ }
2214
+ :is(.mkt, .atelic-ui) .nt-close .op-who {
2215
+ margin-top: 14px;
2216
+ }
2104
2217
  :is(.mkt, .atelic-ui) .nt-finding,
2105
2218
  :is(.mkt, .atelic-ui) .nt-start,
2106
2219
  :is(.mkt, .atelic-ui) .nt-close {