@atelic-action/ui 0.17.0 → 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, sets the two lanes side by side when wide and on paper, 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.0",
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,11 +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: the base rules are the phone's. The two lanes stack,
1869
- each finding a Callout with the owner's screen under it.
1870
- - Wide (721px and up) and print: the two lanes sit side by side.
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.
1871
1882
  - Print: one letter sheet. The chrome steps aside (the writeup's print
1872
1883
  rules already hide the header and the credit band on any artifact
1873
1884
  page), the type comes down, and the screens shrink. The note sets
@@ -1887,10 +1898,12 @@
1887
1898
  color: var(--ink);
1888
1899
  background: var(--surface);
1889
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). */
1890
1903
  :is(.mkt, .atelic-ui) .nt-doc {
1891
- max-width: 760px;
1904
+ max-width: calc(920px + 2 * var(--gutter));
1892
1905
  margin: 0 auto;
1893
- padding: calc(var(--nav-height) + 22px) 18px 48px;
1906
+ padding: calc(var(--nav-height) + 22px) var(--gutter) 48px;
1894
1907
  font-size: 17px;
1895
1908
  line-height: 1.6;
1896
1909
  print-color-adjust: exact;
@@ -1941,7 +1954,8 @@
1941
1954
  text-wrap: pretty;
1942
1955
  }
1943
1956
 
1944
- /* ---- the two lanes, each a heading and its findings ---- */
1957
+ /* ---- the two lanes, each a heading and its findings, one above the
1958
+ other at every width ---- */
1945
1959
  :is(.mkt, .atelic-ui) .nt-lanes {
1946
1960
  display: grid;
1947
1961
  grid-template-columns: minmax(0, 1fr);
@@ -1973,9 +1987,9 @@
1973
1987
  font-size: 16px;
1974
1988
  line-height: 1.5;
1975
1989
  }
1976
- /* The screen sits under its finding, set in from the icon so it lines up
1977
- with the words. It keeps its own shape, never wider than the lane and
1978
- 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. */
1979
1993
  :is(.mkt, .atelic-ui) .nt-finding .op-shot {
1980
1994
  margin: 10px 0 0 28px;
1981
1995
  }
@@ -1986,10 +2000,45 @@
1986
2000
  }
1987
2001
  :is(.mkt, .atelic-ui) .nt-finding .op-shot img {
1988
2002
  width: auto;
1989
- max-width: 100%;
1990
- 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);
1991
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);
2037
+ }
2038
+ /* The caption wraps to the picture's width and never sets the column's. */
1992
2039
  :is(.mkt, .atelic-ui) .nt-finding .op-shot figcaption {
2040
+ width: 0;
2041
+ min-width: 100%;
1993
2042
  font-size: 12.5px;
1994
2043
  line-height: 1.45;
1995
2044
  color: var(--neutral-500);
@@ -2057,21 +2106,49 @@
2057
2106
  text-decoration: none;
2058
2107
  }
2059
2108
 
2060
- /* ---- wide, and on paper: the two lanes side by side ---- */
2061
- @media print, (min-width: 721px) {
2109
+ /* ---- wide: more room, and each screen beside its finding ---- */
2110
+ @media (min-width: 721px) {
2062
2111
  :is(.mkt, .atelic-ui) .nt-doc {
2063
- padding: calc(var(--nav-height) + 36px) 24px 64px;
2112
+ padding-top: calc(var(--nav-height) + 36px);
2113
+ padding-bottom: 64px;
2064
2114
  }
2065
2115
  :is(.mkt, .atelic-ui) .nt-lanes {
2066
- grid-template-columns: repeat(2, minmax(0, 1fr));
2067
2116
  gap: 32px;
2068
2117
  }
2069
2118
  }
2070
2119
 
2071
- /* ---- 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. */
2072
2149
  @media print {
2073
2150
  html:has(:is(.mkt, .atelic-ui) .nt-doc) {
2074
- zoom: 0.8;
2151
+ zoom: 0.74;
2075
2152
  }
2076
2153
  :is(.mkt, .atelic-ui) .nt {
2077
2154
  background: none;
@@ -2080,26 +2157,63 @@
2080
2157
  max-width: none;
2081
2158
  padding: 0;
2082
2159
  font-size: 15px;
2083
- line-height: 1.5;
2160
+ line-height: 1.45;
2161
+ }
2162
+ :is(.mkt, .atelic-ui) .nt h1 {
2163
+ font-size: 32px;
2084
2164
  }
2085
2165
  :is(.mkt, .atelic-ui) .nt-opener {
2166
+ margin-top: 12px;
2086
2167
  font-size: 16px;
2087
2168
  }
2088
2169
  :is(.mkt, .atelic-ui) .nt-lanes,
2089
2170
  :is(.mkt, .atelic-ui) .nt-start,
2090
2171
  :is(.mkt, .atelic-ui) .nt-close {
2091
- margin-top: 20px;
2092
- 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;
2093
2184
  }
2094
2185
  :is(.mkt, .atelic-ui) .nt-finding .callout {
2095
2186
  font-size: 14.5px;
2096
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
+ }
2097
2195
  :is(.mkt, .atelic-ui) .nt-finding .op-shot img {
2098
- 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;
2099
2204
  }
2100
2205
  :is(.mkt, .atelic-ui) .nt-finding .op-shot a {
2101
2206
  cursor: auto;
2102
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
+ }
2103
2217
  :is(.mkt, .atelic-ui) .nt-finding,
2104
2218
  :is(.mkt, .atelic-ui) .nt-start,
2105
2219
  :is(.mkt, .atelic-ui) .nt-close {