A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2 年之前
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>HTML Sketches: Whither Cards? (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://www.colophon.cards/notes/whither-cards/">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>HTML Sketches: Whither Cards?</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://www.colophon.cards/notes/whither-cards/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <style>
  71. .Card {
  72. --border-color: 0 0% 100%;
  73. max-width: 80ch;
  74. padding: 0 0 2rem;
  75. margin: 2rem auto;
  76. border: 0.5rem solid hsl(var(--border-color) / 0.5);
  77. box-shadow: var(--shadow-elevation-medium);
  78. position: relative;
  79. display: grid;
  80. grid-template-columns: 1rem 1fr 1rem;
  81. background-color: #fafafa;
  82. background-color: white;
  83. }
  84. .Card[data-editing] {
  85. box-shadow: var(--shadow-elevation-high);
  86. --border-color: var(--link-normal);
  87. }
  88. .CardBody {
  89. text-align: left;
  90. hyphens: manual;
  91. padding: 1rem 0 2rem;
  92. border: 3px none transparent;
  93. white-space: pre-wrap;
  94. font-family: inherit;
  95. font-size: 100%;
  96. grid-column: 2 / 2;
  97. grid-row: 2 / 2;
  98. margin: 0;
  99. }
  100. .CardTextarea {
  101. text-align: left;
  102. hyphens: manual;
  103. padding: 1rem 0 2rem;
  104. border: 3px none #d5d5c9;
  105. white-space: pre-wrap;
  106. font-family: inherit;
  107. font-size: 100%;
  108. grid-column: 2 / 2;
  109. grid-row: 2 / 2;
  110. margin: 0;
  111. font-variation-settings: inherit;
  112. font-feature-settings: inherit;
  113. background-color: white;
  114. overflow: hidden;
  115. }
  116. .CardDate {
  117. padding: 0.75rem 0rem 0.75rem;
  118. font-size: 0.85rem;
  119. color: #555;
  120. font-variant-caps: all-small-caps;
  121. }
  122. .CardEdit {
  123. position: absolute;
  124. bottom: -0.5rem;
  125. right: -0.5rem;
  126. display: flex;
  127. }
  128. [data-editing] .CardEdit {
  129. --shadow-color: 0 0% 31%;
  130. --shadow-elevation-high: 0.1px 0.3px 0.3px hsl(var(--shadow-color) / 0.4),
  131. 0.7px 1.6px 1.8px -0.4px hsl(var(--shadow-color) / 0.39),
  132. 1.3px 3px 3.4px -0.9px hsl(var(--shadow-color) / 0.37),
  133. 2.3px 5.2px 6px -1.3px hsl(var(--shadow-color) / 0.35),
  134. 3.8px 8.6px 9.9px -1.7px hsl(var(--shadow-color) / 0.34),
  135. 6.2px 13.9px 16px -2.1px hsl(var(--shadow-color) / 0.32),
  136. 9.6px 21.6px 24.8px -2.6px hsl(var(--shadow-color) / 0.3),
  137. 14.3px 32.3px 37.1px -3px hsl(var(--shadow-color) / 0.28);
  138. }
  139. .CardHeader {
  140. display: flex;
  141. justify-content: space-between;
  142. grid-column: 2 / 2;
  143. align-items: center;
  144. color: #555;
  145. }
  146. .CardButtons {
  147. margin: 0;
  148. }
  149. .CardTextButton {
  150. box-sizing: border-box;
  151. border: none;
  152. background-color: transparent;
  153. color: hsl(var(--link-normal));
  154. font-family: inherit;
  155. padding: 0.25rem 0.5rem 0.25rem;
  156. display: inline-flex;
  157. align-items: center;
  158. justify-content: center;
  159. font-variant-caps: all-small-caps;
  160. font-weight: 300;
  161. font-size: 0.85rem;
  162. cursor: pointer;
  163. font-variation-settings: "wght" 600, "opsz" 8;
  164. }
  165. .CardTextButton:hover {
  166. color: hsl(var(--link-hover));
  167. }
  168. .CardButton:hover {
  169. background-color: hsl(var(--link-hover));
  170. color: white;
  171. }
  172. .CardButton {
  173. --border-color: 0 0% 94.1%;
  174. box-sizing: border-box;
  175. border: none;
  176. background-color: transparent;
  177. font-family: inherit;
  178. padding: 0.45rem 2rem 0.5rem;
  179. display: inline-flex;
  180. align-items: center;
  181. justify-content: center;
  182. box-shadow: none;
  183. transition: box-shadow 250ms ease-in-out;
  184. font-variant-caps: all-small-caps;
  185. font-weight: 300;
  186. font-size: 0.85rem;
  187. cursor: pointer;
  188. font-variation-settings: "wght" 600, "opsz" 8;
  189. color: hsl(var(--link-normal));
  190. }
  191. .CardButton svg,
  192. .CardTextButton svg {
  193. margin-right: 1px;
  194. }
  195. [data-editing] .CardButton {
  196. --border-color: 0 0% 94.1%;
  197. box-sizing: border-box;
  198. border: none;
  199. background-color: hsl(var(--border-color));
  200. font-family: inherit;
  201. padding: 0.45rem 2rem 0.5rem;
  202. display: inline-flex;
  203. align-items: center;
  204. justify-content: center;
  205. box-shadow: var(--shadow-elevation-high);
  206. transition: box-shadow 250ms ease-in-out;
  207. font-variant-caps: all-small-caps;
  208. font-weight: 300;
  209. font-size: 0.85rem;
  210. cursor: pointer;
  211. font-variation-settings: "wght" 600, "opsz" 8;
  212. }
  213. .CardButton.Primary {
  214. --link-color: var(--link-normal);
  215. background-color: hsl(var(--link-color));
  216. color: white;
  217. box-shadow: var(--shadow-elevation-high);
  218. }
  219. .CardButton:hover {
  220. background-color: hsl(var(--link-hover));
  221. color: white;
  222. }
  223. .CardButton:active {
  224. box-shadow: var(--shadow-elevation-medium);
  225. }
  226. /* The Single Mode version */
  227. .PlainCard {
  228. --border-color: 0 0% 100%;
  229. max-width: 80ch;
  230. width: 100%;
  231. padding: 0;
  232. margin: 2rem auto;
  233. box-shadow: var(--shadow-elevation-medium);
  234. position: relative;
  235. display: grid;
  236. grid-template-columns: 2rem 1fr 1rem;
  237. background-color: #fafafa;
  238. background-color: white;
  239. grid-column-gap: 0.5rem;
  240. transition: box-shadow 125ms ease-in-out;
  241. }
  242. .PlainCard:focus-within {
  243. box-shadow: var(--shadow-elevation-high);
  244. outline: 3px solid #eee;
  245. }
  246. /*
  247. .Card[data-editing] {
  248. box-shadow: var(--shadow-elevation-high);
  249. --border-color: var(--link-normal);
  250. } */
  251. .PlainCardBody {
  252. text-align: left;
  253. hyphens: manual;
  254. padding: 1rem 2rem 1rem;
  255. border: 3px none transparent;
  256. white-space: pre-wrap;
  257. font-family: inherit;
  258. font-size: 100%;
  259. grid-column: 2 / 2;
  260. grid-row: 2 / 2;
  261. margin: 0;
  262. }
  263. .PlainCardTextarea {
  264. text-align: left;
  265. hyphens: manual;
  266. padding: 1rem 0 1rem;
  267. border: 3px none #d5d5c9;
  268. white-space: pre-wrap;
  269. font-family: inherit;
  270. font-size: 100%;
  271. grid-column: 2 / 2;
  272. grid-row: 2 / 2;
  273. margin: 0;
  274. font-variation-settings: inherit;
  275. font-feature-settings: inherit;
  276. background-color: white;
  277. width: 100%;
  278. box-sizing: border-box;
  279. height: 10rem;
  280. }
  281. .PlainCardEditable {
  282. text-align: left;
  283. hyphens: manual;
  284. padding: 0;
  285. border: 3px none #d5d5c9;
  286. white-space: pre-wrap;
  287. font-family: inherit;
  288. font-size: 100%;
  289. grid-column: 2 / 2;
  290. grid-row: 2 / 2;
  291. margin: 0;
  292. font-variation-settings: inherit;
  293. font-feature-settings: inherit;
  294. background-color: white;
  295. width: 100%;
  296. box-sizing: border-box;
  297. }
  298. .PlainCardDate {
  299. padding: 0.75rem 0rem 0.75rem;
  300. font-size: 0.85rem;
  301. color: #555;
  302. font-variant-caps: all-small-caps;
  303. }
  304. .PlainCardEdit {
  305. position: absolute;
  306. bottom: -0.5rem;
  307. right: -0.5rem;
  308. display: flex;
  309. }
  310. [data-editing] .CardEdit {
  311. --shadow-color: 0 0% 31%;
  312. --shadow-elevation-high: 0.1px 0.3px 0.3px hsl(var(--shadow-color) / 0.4),
  313. 0.7px 1.6px 1.8px -0.4px hsl(var(--shadow-color) / 0.39),
  314. 1.3px 3px 3.4px -0.9px hsl(var(--shadow-color) / 0.37),
  315. 2.3px 5.2px 6px -1.3px hsl(var(--shadow-color) / 0.35),
  316. 3.8px 8.6px 9.9px -1.7px hsl(var(--shadow-color) / 0.34),
  317. 6.2px 13.9px 16px -2.1px hsl(var(--shadow-color) / 0.32),
  318. 9.6px 21.6px 24.8px -2.6px hsl(var(--shadow-color) / 0.3),
  319. 14.3px 32.3px 37.1px -3px hsl(var(--shadow-color) / 0.28);
  320. }
  321. .PlainCardHeader {
  322. display: flex;
  323. justify-content: space-between;
  324. grid-column: 2 / 2;
  325. align-items: center;
  326. color: #555;
  327. font-variation-settings: "wght" 400, "opsz" 8;
  328. }
  329. .PlainCardFooter {
  330. display: flex;
  331. justify-content: space-between;
  332. grid-column: 2 / -1;
  333. align-items: center;
  334. color: #555;
  335. padding: 0 1rem;
  336. }
  337. .PlainCardButtons {
  338. margin: 0;
  339. }
  340. .PlainCardTextButton {
  341. box-sizing: border-box;
  342. border: none;
  343. background-color: transparent;
  344. color: hsl(var(--link-normal));
  345. font-family: inherit;
  346. padding: 0.25rem 0.5rem 0.25rem;
  347. display: inline-flex;
  348. align-items: center;
  349. justify-content: center;
  350. font-variant-caps: all-small-caps;
  351. font-weight: 300;
  352. font-size: 0.85rem;
  353. cursor: pointer;
  354. font-variation-settings: "wght" 600, "opsz" 8;
  355. }
  356. .PlainCardTextButton:hover {
  357. color: hsl(var(--link-hover));
  358. }
  359. .PlainCardButton:hover {
  360. background-color: hsl(var(--link-hover));
  361. color: white;
  362. }
  363. .PlainCardButton {
  364. --border-color: 0 0% 94.1%;
  365. box-sizing: border-box;
  366. border: none;
  367. background-color: hsl(var(--border-color));
  368. font-family: inherit;
  369. padding: 0.45rem 2rem 0.5rem;
  370. display: inline-flex;
  371. align-items: center;
  372. justify-content: center;
  373. box-shadow: var(--shadow-elevation-high);
  374. transition: box-shadow 250ms ease-in-out;
  375. font-variant-caps: all-small-caps;
  376. font-weight: 300;
  377. font-size: 0.85rem;
  378. cursor: pointer;
  379. font-variation-settings: "wght" 600, "opsz" 8;
  380. }
  381. .PlainCardButton svg,
  382. .PlainCardTextButton svg {
  383. margin-right: 1px;
  384. }
  385. .PlainCardButton.Primary {
  386. --link-color: var(--link-normal);
  387. background-color: hsl(var(--link-color));
  388. color: white;
  389. box-shadow: var(--shadow-elevation-high);
  390. }
  391. .PlainCardButton:hover {
  392. background-color: hsl(var(--link-hover));
  393. color: white;
  394. }
  395. .PlainCardButton:active {
  396. box-shadow: var(--shadow-elevation-medium);
  397. }
  398. .PlainCardSidebar {
  399. grid-column: 1 / 1;
  400. grid-row: 1 / 4;
  401. display: flex;
  402. justify-content: space-between;
  403. align-items: center;
  404. color: #555;
  405. flex-direction: column;
  406. padding: 0.5rem 0;
  407. background-color: rgb(248 248 248);
  408. }
  409. .PlainCardGrabHandle {
  410. width: 0;
  411. border: none;
  412. border-left: 4px solid #e1e1e1;
  413. height: 100%;
  414. margin: 2.5rem 0;
  415. }
  416. .PlainCardGrabHandle:hover {
  417. cursor: grab;
  418. }
  419. .PlainCardHeader.Handle {
  420. display: grid;
  421. grid-template-columns: 1fr 1fr 1fr;
  422. }
  423. .PlainCardGrabHandle.Header {
  424. width: 5rem;
  425. border: none;
  426. margin: 0 auto;
  427. height: 0.5rem;
  428. display: inline-flex;
  429. padding: calc(0.25rem + 4px) 0.5rem 0.25rem;
  430. border-radius: 8px;
  431. }
  432. .PlainCardGrabHandle.Header:hover {
  433. background-color: #f0f0f0;
  434. }
  435. .PlainCardGrabHandle.Header::before {
  436. content: "";
  437. border-top: 4px solid #ccc;
  438. height: 0.25rem;
  439. width: 100%;
  440. display: block;
  441. margin: auto;
  442. }
  443. .CardNumber {
  444. text-align: right;
  445. }
  446. /* The Single Mode version */
  447. .FieldCard {
  448. --border-color: 0 0% 100%;
  449. max-width: 80ch;
  450. width: 100%;
  451. padding: 0;
  452. margin: 2rem auto;
  453. box-shadow: var(--shadow-elevation-medium);
  454. position: relative;
  455. display: grid;
  456. grid-template-columns: 2rem 1fr 1rem;
  457. background-color: #fafafa;
  458. background-color: white;
  459. grid-gap: 0.5rem;
  460. transition: box-shadow 125ms ease-in-out;
  461. }
  462. .FieldCard:focus-within {
  463. box-shadow: var(--shadow-elevation-high);
  464. outline: 3px solid #eee;
  465. }
  466. /*
  467. .Card[data-editing] {
  468. box-shadow: var(--shadow-elevation-high);
  469. --border-color: var(--link-normal);
  470. } */
  471. .FieldCardSummary {
  472. grid-column: 2 / 2;
  473. }
  474. .FieldCardLabel {
  475. font-size: 0.85rem;
  476. cursor: pointer;
  477. font-variation-settings: "wght" 600, "opsz" 8;
  478. font-variant-caps: all-small-caps;
  479. display: block;
  480. }
  481. .FieldCardInput {
  482. text-align: left;
  483. hyphens: manual;
  484. padding: 0.25rem;
  485. border: 1px solid #f0f0f0;
  486. white-space: pre-wrap;
  487. font-family: inherit;
  488. font-size: 100%;
  489. margin: 0;
  490. font-variation-settings: inherit;
  491. font-feature-settings: inherit;
  492. background-color: white;
  493. width: 100%;
  494. box-sizing: border-box;
  495. }
  496. .FieldCardBody {
  497. text-align: left;
  498. hyphens: manual;
  499. padding: 1rem 2rem 1rem;
  500. border: 3px none transparent;
  501. white-space: pre-wrap;
  502. font-family: inherit;
  503. font-size: 100%;
  504. margin: 0;
  505. }
  506. .FieldCardEditable {
  507. text-align: left;
  508. hyphens: manual;
  509. padding: 0;
  510. padding: 0.25rem;
  511. border: 1px solid #f0f0f0;
  512. white-space: pre-wrap;
  513. font-family: inherit;
  514. font-size: 100%;
  515. grid-column: 2 / 2;
  516. margin: 0;
  517. font-variation-settings: inherit;
  518. font-feature-settings: inherit;
  519. background-color: white;
  520. width: 100%;
  521. box-sizing: border-box;
  522. }
  523. .FieldCardDate {
  524. padding: 0.75rem 0rem 0.75rem;
  525. font-size: 0.85rem;
  526. color: #555;
  527. font-variant-caps: all-small-caps;
  528. }
  529. .FieldCardEdit {
  530. position: absolute;
  531. bottom: -0.5rem;
  532. right: -0.5rem;
  533. display: flex;
  534. }
  535. [data-editing] .CardEdit {
  536. --shadow-color: 0 0% 31%;
  537. --shadow-elevation-high: 0.1px 0.3px 0.3px hsl(var(--shadow-color) / 0.4),
  538. 0.7px 1.6px 1.8px -0.4px hsl(var(--shadow-color) / 0.39),
  539. 1.3px 3px 3.4px -0.9px hsl(var(--shadow-color) / 0.37),
  540. 2.3px 5.2px 6px -1.3px hsl(var(--shadow-color) / 0.35),
  541. 3.8px 8.6px 9.9px -1.7px hsl(var(--shadow-color) / 0.34),
  542. 6.2px 13.9px 16px -2.1px hsl(var(--shadow-color) / 0.32),
  543. 9.6px 21.6px 24.8px -2.6px hsl(var(--shadow-color) / 0.3),
  544. 14.3px 32.3px 37.1px -3px hsl(var(--shadow-color) / 0.28);
  545. }
  546. .FieldCardHeader {
  547. display: flex;
  548. justify-content: space-between;
  549. grid-column: 2 / 2;
  550. align-items: center;
  551. color: #555;
  552. font-variation-settings: "wght" 400, "opsz" 8;
  553. }
  554. .FieldCardFooter {
  555. display: flex;
  556. justify-content: space-between;
  557. grid-column: 2 / -1;
  558. align-items: center;
  559. color: #555;
  560. padding: 0 1rem;
  561. }
  562. .FieldCardButtons {
  563. margin: 0;
  564. }
  565. .FieldCardTextButton {
  566. box-sizing: border-box;
  567. border: none;
  568. background-color: transparent;
  569. color: hsl(var(--link-normal));
  570. font-family: inherit;
  571. padding: 0.25rem 0.5rem 0.25rem;
  572. display: inline-flex;
  573. align-items: center;
  574. justify-content: center;
  575. font-variant-caps: all-small-caps;
  576. font-weight: 300;
  577. font-size: 0.85rem;
  578. cursor: pointer;
  579. font-variation-settings: "wght" 600, "opsz" 8;
  580. }
  581. .FieldCardTextButton:hover {
  582. color: hsl(var(--link-hover));
  583. }
  584. .FieldCardButton:hover {
  585. background-color: hsl(var(--link-hover));
  586. color: white;
  587. }
  588. .FieldCardButton {
  589. --border-color: 0 0% 94.1%;
  590. box-sizing: border-box;
  591. border: none;
  592. background-color: hsl(var(--border-color));
  593. font-family: inherit;
  594. padding: 0.45rem 2rem 0.5rem;
  595. display: inline-flex;
  596. align-items: center;
  597. justify-content: center;
  598. box-shadow: var(--shadow-elevation-high);
  599. transition: box-shadow 250ms ease-in-out;
  600. font-variant-caps: all-small-caps;
  601. font-weight: 300;
  602. font-size: 0.85rem;
  603. cursor: pointer;
  604. font-variation-settings: "wght" 600, "opsz" 8;
  605. }
  606. .FieldCardButton svg,
  607. .FieldCardTextButton svg {
  608. margin-right: 1px;
  609. }
  610. .FieldCardButton.Primary {
  611. --link-color: var(--link-normal);
  612. background-color: hsl(var(--link-color));
  613. color: white;
  614. box-shadow: var(--shadow-elevation-high);
  615. }
  616. .FieldCardButton:hover {
  617. background-color: hsl(var(--link-hover));
  618. color: white;
  619. }
  620. .FieldCardButton:active {
  621. box-shadow: var(--shadow-elevation-medium);
  622. }
  623. .FieldCardSidebar {
  624. grid-column: 1 / 1;
  625. grid-row: 1 / 4;
  626. display: flex;
  627. justify-content: space-between;
  628. align-items: center;
  629. color: #555;
  630. flex-direction: column;
  631. padding: 0.5rem 0;
  632. background-color: rgb(248 248 248);
  633. }
  634. .FieldCardGrabHandle {
  635. width: 0;
  636. border: none;
  637. border-left: 4px solid #e1e1e1;
  638. height: 100%;
  639. margin: 2.5rem 0;
  640. }
  641. .FieldCardGrabHandle:hover {
  642. cursor: grab;
  643. }
  644. .FieldCardHeader.Handle {
  645. display: grid;
  646. grid-template-columns: 1fr 1fr 1fr;
  647. }
  648. .FieldCardGrabHandle.Header {
  649. width: 5rem;
  650. border: none;
  651. margin: 0 auto;
  652. height: 0.5rem;
  653. display: inline-flex;
  654. padding: calc(0.25rem + 4px) 0.5rem 0.25rem;
  655. border-radius: 8px;
  656. }
  657. .FieldCardGrabHandle.Header:hover {
  658. background-color: #f0f0f0;
  659. }
  660. .FieldCardGrabHandle.Header::before {
  661. content: "";
  662. border-top: 4px solid #ccc;
  663. height: 0.25rem;
  664. width: 100%;
  665. display: block;
  666. margin: auto;
  667. }
  668. .CardNumber {
  669. text-align: right;
  670. }
  671. /* The Single Mode version */
  672. .ModeCard {
  673. --border-color: 0 0% 100%;
  674. max-width: 80ch;
  675. width: 100%;
  676. padding: 0;
  677. margin: 2rem auto;
  678. box-shadow: var(--shadow-elevation-medium);
  679. position: relative;
  680. display: grid;
  681. grid-template-columns: 2rem 1fr 1rem;
  682. background-color: #fafafa;
  683. background-color: white;
  684. grid-gap: 0.5rem;
  685. transition: box-shadow 125ms ease-in-out;
  686. --shadow-color: 0deg 0% 0%;
  687. --shadow-elevation-low: 0.3px 0.5px 0.7px hsl(var(--shadow-color) / 0.11),
  688. 0.4px 0.9px 1.1px -1.2px hsl(var(--shadow-color) / 0.11),
  689. 1px 2.1px 2.6px -2.5px hsl(var(--shadow-color) / 0.11);
  690. --shadow-elevation-medium: 0.3px 0.5px 0.7px hsl(var(--shadow-color) / 0.12),
  691. 0.9px 1.7px 2.2px -0.8px hsl(var(--shadow-color) / 0.12),
  692. 2.1px 4.3px 5.4px -1.7px hsl(var(--shadow-color) / 0.12),
  693. 5.3px 10.5px 13.2px -2.5px hsl(var(--shadow-color) / 0.12);
  694. --shadow-elevation-high: 0.3px 0.5px 0.7px hsl(var(--shadow-color) / 0.11),
  695. 1.6px 3.2px 4px -0.4px hsl(var(--shadow-color) / 0.11),
  696. 3px 5.9px 7.4px -0.7px hsl(var(--shadow-color) / 0.11),
  697. 4.9px 9.8px 12.3px -1.1px hsl(var(--shadow-color) / 0.11),
  698. 7.8px 15.7px 19.7px -1.4px hsl(var(--shadow-color) / 0.11),
  699. 12.3px 24.5px 30.8px -1.8px hsl(var(--shadow-color) / 0.11),
  700. 18.7px 37.3px 46.9px -2.1px hsl(var(--shadow-color) / 0.11),
  701. 27.5px 55px 69.2px -2.5px hsl(var(--shadow-color) / 0.11);
  702. }
  703. .ModeCard:focus-within {
  704. box-shadow: var(--shadow-elevation-high);
  705. outline: 3px solid #eee;
  706. }
  707. /*
  708. .Card[data-editing] {
  709. box-shadow: var(--shadow-elevation-high);
  710. --border-color: var(--link-normal);
  711. } */
  712. .ModeCardSummary {
  713. grid-column: 2 / 2;
  714. }
  715. .ModeCardLabel {
  716. font-size: 0.85rem;
  717. cursor: pointer;
  718. font-variation-settings: "wght" 600, "opsz" 8;
  719. font-variant-caps: all-small-caps;
  720. display: block;
  721. }
  722. .ModeCardName {
  723. font-size: 1rem;
  724. cursor: pointer;
  725. font-variation-settings: "wght" 600, "opsz" 8;
  726. font-variant-caps: normal;
  727. display: block;
  728. grid-column: 2 / 2;
  729. margin: 0.25rem 0 1rem;
  730. }
  731. /*
  732. .ModeCard input {
  733. text-align: left;
  734. hyphens: manual;
  735. padding: 0;
  736. border: 1px solid #f0f0f0;
  737. white-space: pre-wrap;
  738. font-family: inherit;
  739. font-size: 100%;
  740. margin: 0;
  741. font-variation-settings: inherit;
  742. font-feature-settings: inherit;
  743. background-color: white;
  744. width: 100%;
  745. box-sizing: border-box;
  746. } */
  747. .ModeCardBody {
  748. text-align: left;
  749. hyphens: manual;
  750. padding: 0;
  751. white-space: pre-wrap;
  752. font-family: inherit;
  753. font-size: 100%;
  754. margin: 0;
  755. grid-column: 2 / 2;
  756. margin-bottom: 1rem;
  757. }
  758. .ModeCardEditable {
  759. text-align: left;
  760. hyphens: manual;
  761. padding: 0;
  762. padding: 0.25rem;
  763. border: 1px solid #f0f0f0;
  764. white-space: pre-wrap;
  765. font-family: inherit;
  766. font-size: 100%;
  767. grid-column: 2 / 2;
  768. margin: 0;
  769. font-variation-settings: inherit;
  770. font-feature-settings: inherit;
  771. background-color: white;
  772. width: 100%;
  773. box-sizing: border-box;
  774. height: 7rem;
  775. }
  776. .ModeCardDate {
  777. padding: 0.75rem 0rem 0.75rem;
  778. font-size: 0.85rem;
  779. color: #555;
  780. font-variant-caps: all-small-caps;
  781. }
  782. .ModeCardEdit {
  783. position: absolute;
  784. bottom: -0.5rem;
  785. right: -0.5rem;
  786. display: flex;
  787. }
  788. [data-editing] .CardEdit {
  789. --shadow-color: 0 0% 31%;
  790. --shadow-elevation-high: 0.1px 0.3px 0.3px hsl(var(--shadow-color) / 0.4),
  791. 0.7px 1.6px 1.8px -0.4px hsl(var(--shadow-color) / 0.39),
  792. 1.3px 3px 3.4px -0.9px hsl(var(--shadow-color) / 0.37),
  793. 2.3px 5.2px 6px -1.3px hsl(var(--shadow-color) / 0.35),
  794. 3.8px 8.6px 9.9px -1.7px hsl(var(--shadow-color) / 0.34),
  795. 6.2px 13.9px 16px -2.1px hsl(var(--shadow-color) / 0.32),
  796. 9.6px 21.6px 24.8px -2.6px hsl(var(--shadow-color) / 0.3),
  797. 14.3px 32.3px 37.1px -3px hsl(var(--shadow-color) / 0.28);
  798. }
  799. .ModeCardHeader {
  800. display: flex;
  801. justify-content: space-between;
  802. grid-column: 2 / 2;
  803. align-items: center;
  804. color: #555;
  805. font-variation-settings: "wght" 400, "opsz" 8;
  806. }
  807. .ModeCardFooter {
  808. display: flex;
  809. justify-content: space-between;
  810. grid-column: 2 / 2;
  811. align-items: center;
  812. color: #555;
  813. padding: 0;
  814. }
  815. .ModeCardButtons {
  816. margin: 0;
  817. }
  818. .ModeCardTextButton {
  819. box-sizing: border-box;
  820. border: none;
  821. background-color: transparent;
  822. color: hsl(var(--link-normal));
  823. font-family: inherit;
  824. padding: 0.25rem 0.5rem 0.25rem;
  825. display: inline-flex;
  826. align-items: center;
  827. justify-content: center;
  828. font-variant-caps: all-small-caps;
  829. font-weight: 300;
  830. font-size: 0.85rem;
  831. cursor: pointer;
  832. font-variation-settings: "wght" 600, "opsz" 8;
  833. }
  834. .ModeCardTextButton:hover {
  835. color: hsl(var(--link-hover));
  836. }
  837. .ModeCardButton:hover {
  838. background-color: hsl(var(--link-hover));
  839. color: white;
  840. }
  841. .ModeCardButton {
  842. --border-color: 0 0% 94.1%;
  843. box-sizing: border-box;
  844. border: none;
  845. background-color: hsl(var(--border-color));
  846. font-family: inherit;
  847. padding: 0.45rem 2rem 0.5rem;
  848. display: inline-flex;
  849. align-items: center;
  850. justify-content: center;
  851. box-shadow: var(--shadow-elevation-high);
  852. transition: box-shadow 250ms ease-in-out;
  853. font-variant-caps: all-small-caps;
  854. font-weight: 300;
  855. font-size: 0.85rem;
  856. cursor: pointer;
  857. font-variation-settings: "wght" 600, "opsz" 8;
  858. }
  859. .ModeCardButton svg,
  860. .ModeCardTextButton svg {
  861. margin-right: 1px;
  862. }
  863. .ModeCardButton.Primary {
  864. --link-color: var(--link-normal);
  865. background-color: hsl(var(--link-color));
  866. color: white;
  867. box-shadow: var(--shadow-elevation-high);
  868. }
  869. .ModeCardButton:hover {
  870. background-color: hsl(var(--link-hover));
  871. color: white;
  872. }
  873. .ModeCardButton:active {
  874. box-shadow: var(--shadow-elevation-medium);
  875. }
  876. .ModeCardSidebar {
  877. grid-column: 1 / 1;
  878. grid-row: 1 / 5;
  879. display: flex;
  880. justify-content: space-between;
  881. align-items: center;
  882. color: #555;
  883. flex-direction: column;
  884. padding: 0.5rem 0;
  885. background-color: rgb(248 248 248);
  886. }
  887. .ModeCardGrabHandle {
  888. width: 0;
  889. border: none;
  890. border-left: 4px solid #e1e1e1;
  891. height: 100%;
  892. margin: 2.5rem 0;
  893. }
  894. .ModeCardGrabHandle:hover {
  895. cursor: grab;
  896. }
  897. .ModeCardHeader.Handle {
  898. display: grid;
  899. grid-template-columns: 1fr 5.5rem;
  900. }
  901. .ModeCardGrabHandle.Header {
  902. width: 5rem;
  903. border: none;
  904. margin: 0 auto;
  905. height: 0.5rem;
  906. display: inline-flex;
  907. padding: calc(0.25rem + 4px) 0.5rem 0.25rem;
  908. border-radius: 8px;
  909. }
  910. .ModeCardGrabHandle.Header:hover {
  911. background-color: #f0f0f0;
  912. }
  913. .ModeCardGrabHandle.Header::before {
  914. content: "";
  915. border-top: 4px solid #ccc;
  916. height: 0.25rem;
  917. width: 100%;
  918. display: block;
  919. margin: auto;
  920. }
  921. .CardNumber {
  922. text-align: right;
  923. }
  924. .ModeCardLinks {
  925. grid-column: 2 / 2;
  926. padding: 0.25rem 0.25rem;
  927. }
  928. .ModeCardAttachments {
  929. grid-column: 2 / 2;
  930. padding: 0;
  931. }
  932. .ModeCardLink {
  933. box-shadow: var(--shadow-elevation-low);
  934. padding: 0.5rem;
  935. padding-left: 4rem;
  936. background-color: #f9f9f9;
  937. border: 1px solid #f0f0f0;
  938. border-radius: 8px;
  939. margin: 0.5rem 0;
  940. }
  941. .ModeCardLink.Internal {
  942. background-color: white;
  943. border-radius: 0px;
  944. }
  945. .ModeCardLink.Attachment {
  946. background-color: white;
  947. border-radius: 0px;
  948. margin: 0.5rem -0.5rem;
  949. margin-right: -1.5rem;
  950. }
  951. /** --------------------------------------------------------------------
  952. ARIA Switch Toggle Button
  953. --------------------------------------------------------------------- */
  954. .switch-toggle {
  955. background: none;
  956. border: none;
  957. cursor: default;
  958. display: block;
  959. font-size: inherit;
  960. line-height: 1;
  961. margin: 1em 0 0;
  962. padding: 0.5em 0;
  963. position: relative;
  964. text-align: left;
  965. transition: box-shadow 0.2s ease-in-out;
  966. width: 100%;
  967. }
  968. .switch-toggle:focus {
  969. outline: none;
  970. }
  971. /* negate 'flash' of text color when pressing a button in some browsers */
  972. .switch-toggle:active {
  973. color: inherit;
  974. }
  975. /* using the before/after pseudo elements of the span to create the "switch" */
  976. .switch-toggle span:before,
  977. .switch-toggle span:after {
  978. border: 1px solid #565656;
  979. content: "";
  980. position: absolute;
  981. top: 50%;
  982. transform: translateY(-50%);
  983. }
  984. /* styling specific to the knob of the switch */
  985. .switch-toggle span:after {
  986. background: #fff;
  987. border-radius: 100%;
  988. height: 1.5em;
  989. right: 1.5em;
  990. transition: right 0.1825s ease-in-out;
  991. width: 1.5em;
  992. }
  993. /* styling specific to the knob "container" */
  994. .switch-toggle span:before {
  995. background: #eee;
  996. border-radius: 1.75em;
  997. height: 1.75em;
  998. right: 0.25em;
  999. transition: background 0.2s ease-in-out;
  1000. width: 2.75em;
  1001. }
  1002. .switch-toggle span {
  1003. pointer-events: none;
  1004. }
  1005. .switch-toggle:not([data-keep-disabled]):hover span:before,
  1006. .switch-toggle:not([data-keep-disabled]):focus span:before {
  1007. outline: 2px solid #2196f3;
  1008. outline-offset: 2px;
  1009. }
  1010. /* change the position of the knob to indicate it has been checked*/
  1011. .switch-toggle[aria-checked="true"] span:after {
  1012. right: 0.25em;
  1013. }
  1014. /* update the color of the "container" to further visually indicate state */
  1015. .switch-toggle[aria-checked="true"] span:before {
  1016. background: #2196f3;
  1017. }
  1018. .switch-toggle[disabled] span,
  1019. .switch-toggle[aria-disabled="true"] span {
  1020. opacity: 0.65;
  1021. }
  1022. /**
  1023. * Toggle switch modifications for displaying On/Off labels
  1024. */
  1025. .switch-toggle .show-labels:before {
  1026. content: "Off";
  1027. color: rgba(10, 10, 10, 1);
  1028. line-height: 1.6;
  1029. text-indent: 1.625em;
  1030. width: 3.5em;
  1031. }
  1032. .switch-toggle[aria-checked="false"] .show-labels:after {
  1033. right: 2.2em;
  1034. }
  1035. .switch-toggle[aria-checked="true"] .show-labels:before {
  1036. color: rgba(255, 255, 255, 1);
  1037. content: "On";
  1038. text-indent: 0.25em;
  1039. text-shadow: 0 0 1px #000;
  1040. }
  1041. @media screen and (-ms-high-contrast: active) {
  1042. .switch-toggle span:after {
  1043. background-color: windowText;
  1044. }
  1045. }
  1046. /* containing label */
  1047. .check-switch {
  1048. display: block;
  1049. overflow: hidden;
  1050. padding: 0.5em 0;
  1051. position: relative;
  1052. }
  1053. /* using the before/after pseudo elements of the span to create the "switch" */
  1054. .check-switch span:before,
  1055. .check-switch span:after {
  1056. border: 1px solid #565656;
  1057. content: "";
  1058. position: absolute;
  1059. top: 50%;
  1060. transform: translateY(-50%);
  1061. }
  1062. /* styling specific to the knob of the switch */
  1063. .check-switch span:after {
  1064. background: #fff;
  1065. border-radius: 100%;
  1066. height: 1.5em;
  1067. right: 1.5em;
  1068. transition: right 0.1825s ease-in-out;
  1069. width: 1.5em;
  1070. }
  1071. /* styling specific to the knob "container" */
  1072. .check-switch span:before {
  1073. background: #eee;
  1074. border-radius: 1.75em;
  1075. height: 1.75em;
  1076. right: 0.25em;
  1077. transition: background 0.2s ease-in-out;
  1078. width: 2.75em;
  1079. }
  1080. /* hide the actual checkbox from view, but not from keyboards or ATs.
  1081. Instead of standard visually hidden styling, instead set opacity to
  1082. to 0 and set to full height/width of container element so that VO focus
  1083. ring outlines the component, instead of a tiny box within the component.
  1084. This will also allow for the checkbox input to be discovered if someone
  1085. were to search by touch.
  1086. */
  1087. .check-switch input {
  1088. height: 100%;
  1089. left: 0;
  1090. opacity: 0;
  1091. position: absolute;
  1092. top: 0;
  1093. width: 100%;
  1094. }
  1095. .check-switch input:disabled + span,
  1096. .check-switch input[aria-disabled="true"] + span {
  1097. opacity: 0.65;
  1098. }
  1099. .check-switch:hover input:not([data-keep-disabled]) + span:before,
  1100. .check-switch input:not([data-keep-disabled]):focus + span:before {
  1101. outline: 2px solid #2196f3;
  1102. outline-offset: 2px;
  1103. }
  1104. /* change the position of the knob to indicate it has been checked*/
  1105. .check-switch input:checked + span:after {
  1106. right: 0.25em;
  1107. }
  1108. /* update the color of the "container" to further visually indicate state */
  1109. .check-switch input:checked + span:before {
  1110. background: #2196f3;
  1111. }
  1112. /* 'color in' the switch knob in high contrast mode by giving it
  1113. a large border */
  1114. @media screen and (-ms-high-contrast: active) {
  1115. .check-switch span:after {
  1116. background-color: windowText;
  1117. }
  1118. }
  1119. </style>
  1120. <p><strong>Warning: this post contains mockups done in HTML and CSS which aren’t likely to work well in feed readers.</strong></p>
  1121. <p>The list in the previous post has a lot of <a href="https://www.colophon.cards/notes/10-questions/">open questions</a> related to cards. Now, there are a lot of approaches to answering these sorts of questions—research, surveys, testing—but in this specific context I think it would be a mistake to dive right into one of those.</p>
  1122. <ul>
  1123. <li>I’ve already done a lot of research on this, it’s been a big part of my life for the past five years</li>
  1124. <li>I’ve deliberately chosen a target audience of which I am somewhat representative (knowledge worker in tech or publishing)</li>
  1125. <li>I may have the questions I need answered but I don’t have the questions I need to ask</li>
  1126. </ul>
  1127. <p>It’s the last one that’s tricky. If you ask questions that people don’t know the answer to or is outside of their expertise, most of the time you’re just going to get whatever they think you want to hear. The best thing to do is to observe their behaviour as they are doing the work you are aiming to help with. See what problems crop up. Things get difficult if you can’t do that but you can somewhat make do if, say, you are a part of the audience you are aiming to serve. But you still need to figure out if the audience understands the conceptual model of the UIs you are planning to use, if it actually does the job it’s supposed to, if the job it’s supposed to do actually solves the problem they have.</p>
  1128. <p>To do that I need to be clearer about what I’m asking. I need to clarify, in my mind, what <em>I</em> mean when I say ‘card’.</p>
  1129. <p>This is where sketches come in. Some use special apps for doing these kind of sketch mockups. Some just sketch it, literally. I like to sketch in HTML and CSS as that’s a lot quicker and simpler for me than most of these other tools.</p>
  1130. <p>So, the goal is to sketch out a few approaches and iteratively inch my way towards a cohesive card concept that will then become the basis of future testing, research, and work.</p>
  1131. <h2>Plain text with modes</h2>
  1132. <p>First up is plain text, first line of the card body serves as a summary, the rest is the card detail. Separate modes <em>on card</em> for editing and viewing.</p>
  1133. <div class="Card">
  1134. <p class="CardBody">This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1135.  
  1136. Is markdown a useful compromise?
  1137. </p></div>
  1138. <div class="Card" data-editing>
  1139. <p class="CardBody">This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1140.  
  1141. Is markdown a useful compromise?</p>
  1142. </div>
  1143. <p>Ugh.</p>
  1144. <p>How about we just assume I get autosaving working and skip the whole “Cancel” and “Save” thing?</p>
  1145. <div class="Card" data-editing>
  1146. <p class="CardBody">This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1147.  
  1148. Is markdown a useful compromise?</p>
  1149. </div>
  1150. <p>Then the issue is <em>how do you exit the edit mode?</em> Automatically exiting edit mode whenever the card loses focus might be one way, but that would get frustrating quickly.</p>
  1151. <h2>Plain text, always edit</h2>
  1152. <p>What about just having the card always in edit mode? That’s how most native apps work. You don’t have to click ‘edit’ anywhere in Bear to edit a note.</p>
  1153. <p>So, plain text, first line of the card body serves as a summary, the rest is the card detail. Always editing but with autosave of some sort.</p>
  1154. <p>Always being in the edit mode has issues when it comes to interacting with links and references but, provided that it’s the right overall direction, I think we might be able to get around that using link previews much like you see in Facebook’s posting widget or by using tooltips like Google Docs. It would be <em>hassle</em> and make accessibility a bit more complicated, but overall doable.</p>
  1155. <p><em>If</em> the single-mode direction is worth it, overall.</p>
  1156. <p>So…</p>
  1157. <p class="PlainCard" data-editing>
  1158. </p>
  1159. <p>This feels a bit cluttered but most of the buttons are just guesses at the sort of functionality a card might have. But, one notion that I think might be a useful guide here is <a href="https://www.taskpaper.com/">Taskpaper’s idea</a> that the note’s text is the API so links are just links in the text. Possibly extracted and presented with preview widgets as you see on Twitter or Facebook. But either way it doesn’t need a button. And the star thing was just a whim. Let’s drop both and clean things up a little bit.</p>
  1160. <p>I can tweak the implementation of the textarea a bit as well. Use a simple <code>contenteditable</code> to make it autogrowing.</p>
  1161. <div class="PlainCard" data-editing>
  1162. <p class="PlainCardEditable" contenteditable>This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1163.  
  1164. Is markdown a useful compromise?</p>
  1165. </div>
  1166. <p>How to handle deletes, archiving, or trashing of a card is its own set of problems. But I’m thinking that it might actually belong in the margin or outside the card in some way.</p>
  1167. <p>Something like this (if you’ll excuse the poor snap):</p>
  1168. <p><img src="https://www.colophon.cards/assets/trash-sketch.jpg" alt="Quick sketch of trash button in the sidebar next to a card. Shows that the idea is that the card would animate out into the margin when the button is pressed"></p>
  1169. <p>So:</p>
  1170. <div class="PlainCard" data-editing>
  1171. <p class="PlainCardEditable" contenteditable>This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1172.  
  1173. Is markdown a useful compromise?</p>
  1174. </div>
  1175. <p>The idea with the grab handle is that you should be able to rearrange the order of the cards in a thread/stack and even drag it out into the sidebar and position as you wish. You need it as the card body itself is always editable so you can’t make the <em>whole</em> card draggable. This feels like a core affordance for the app so, until testing reveals otherwise, I’m going to keep it in the sketches.</p>
  1176. <p>But it doesn’t have to be where it is here. It could be placed in the empty space in the header.</p>
  1177. <div class="PlainCard" data-editing>
  1178. <p class="PlainCardEditable" contenteditable>This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will plaintext do?
  1179.  
  1180. Is markdown a useful compromise?</p>
  1181. </div>
  1182. <p>This could work but I honestly don’t think it’s objectively superior to the margin grab handle.</p>
  1183. <p>Either way, the grab handle isn’t even close to being an intuitive affordance and will need a complete rethink and design but this will do while sketching to get a feel for the card problem and figure out what to test.</p>
  1184. <h2>But, wait, this doesn’t work</h2>
  1185. <p>All of the above is useful for hashing out certain themes and directions</p>
  1186. <p>Now I know that I need to:</p>
  1187. <ul>
  1188. <li>Avoid the Edit → Save cycle. Maybe not avoid the edit mode, per se.</li>
  1189. <li>Avoid <code>save</code> gestures and loops in general. Autosave needs to work.</li>
  1190. <li>Use the card’s text as an API surface for links, much like social media does. (Leverage familiarity.)</li>
  1191. <li>Consider trash, archiving, and delete <em>in context</em>, which means we need to tackle it in the thread/stack view.</li>
  1192. <li>The grab handle needs more work, probably in the thread context, like deletions.</li>
  1193. </ul>
  1194. <p>The problem with the above sketches is that they wouldn’t work with the app’s concept as I’ve planned it.</p>
  1195. <p>Namely, the concept is that it’s a ‘find first’ UI. Each card is supposed to have a unique name or summary. With a single body card, the first 500 or so characters* would serve as the card’s summary.</p>
  1196. <aside>
  1197. <p><em>* Still a guess but, based on the DB and indexes available, the responsiveness this kind of UI needs, and the fact that with emojis even bog standard English needs to support multi-byte characters, ~500 characters is the ballpark maximum. I could go over but that’d either limit my DB choices when it comes to implementation or throw internationalisation out of the window or limit the find feature to just the first 500 or so characters of the summary.</em></p>
  1198. </aside>
  1199. <p>The problem with single-body cards is that the app needs to validate the card’s summary: it has to be unique. There’s also the question as to whether you’d allow <code>null</code> or anonymous cards. These would have an internal DB identifier but no external name/summary. Basically, these would be cards that you could only find through a full-text search at some point down the line or by scrolling to its position in the thread. Anonymous cards <em>could</em> be useful. But it could potentially make a clumsy ‘select all’ disastrous. I semi-regularly delete an entire file’s contents by accident this way. Not much of an issue when you have undos and version control but a major issue if doing so made the file <em>unfindable</em>. It’s much harder to revert changes to a file <em>if you can’t find it</em>.</p>
  1200. <p>It’s likely that we’re going to need a separate field for the name or summary and it’s certain that this field is going to need stricter validation than the details or body field.</p>
  1201. <p>I’m going to need to sketch out a slightly more realistic card.</p>
  1202. <p>Even without links, attachments, or cross-references, this feels more clunky. You could finesse the design, streamline it here and there, but if we’re going to have the card always editable then this would mean a fairly high inherent ‘clunkiness’ baseline that you can’t avoid.</p>
  1203. <p>I think it’s clear that we’ll need an edit mode. Can the edit mode be made nicer if we assume I can get autosaving to work nicely?</p>
  1204. <h2>Revisiting the “Edit Mode” and makig it nicer</h2>
  1205. <p>Having separate “read/browse” and “edit” modes gives me a little bit more freedom when designing each version. I still think they need to be very similar, for usability’s sake. This one’s using <a href="https://scottaohara.github.io/a11y_styled_form_controls/src/checkbox--switch/">Scott O’Hara’s switch component</a> though since this is just an HTML mockup it doesn’t do anything.</p>
  1206. <div class="ModeCard" data-editing>
  1207. <h3 class="ModeCardName">You'll use this to find this card</h3>
  1208. <div class="ModeCardBody"><p>This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will </p><a href="#">plaintext</a><p> do?
  1209.  
  1210. Is markdown a useful compromise?</p></div>
  1211. </div>
  1212. <p>One issue that this card sketch doesn’t highlight is that the card-and-thread model is supposed to offer similar affordances as many social media services, like Twitter or Facebook. Those services have both highly optimised posting flows (they want to make it really, really easy for you to post quickly) and are also familiar to many. But the creation of a full card with title <em>feels</em> a bit off for a default in this context. You pretty much need to be able to create the simple kind of plain text cards that I sketched up above but without the anonymity issue that would prevent cards from being found or referred to.</p>
  1213. <p>This is why I prefer the summary label for the card’s ‘name’, at least for the moment. It implies a potentially longer text than ‘name’. (<a href="https://twitter.com/John_Pettigrew/status/1466008916307894277">Thanks for the suggestion, John!</a>) So, I could support a slightly different style for cards that only have a summary and no text in the body.</p>
  1214. <div class="ModeCard" data-editing>
  1215. <p class="ModeCardBody">This is a card summary. As a summary it would work more like a tweet would and wouldn't support Markdown or rich text.</p>
  1216. </div>
  1217. <p>With edit mode on:</p>
  1218. <p>Edit mode would need to validate the summary on the fly, make sure it isn’t empty and that it is unique. And it could lock the card in edit mode if you enter an invalid summary.</p>
  1219. <p>This is aesthetically a bit rough but it feels right as an initial conceptual model. But I need to see it with more ‘stuff’ to be sure. Some links and attachments.</p>
  1220. <div class="ModeCard" data-editing>
  1221. <h3 class="ModeCardName">You'll use this to find this card</h3>
  1222. <div class="ModeCardBody"><p>This is a card body. Or card text. What does this look like? What does this feel like? How does this work? Does it need rich text? Will </p><a href="#">plaintext</a><p> do?
  1223.  
  1224. Is markdown a useful compromise?</p></div>
  1225. <div class="ModeCardLinks">
  1226. <div class="ModeCardLink">
  1227. <p class="LinkDescription">With an example description of that link</p>
  1228. </div>
  1229. <div class="ModeCardLink Internal">
  1230. <p class="LinkDescription">With an example description of that card</p>
  1231. </div>
  1232. <div class="ModeCardLink">
  1233. <p class="LinkDescription">With an example description of that file</p>
  1234. </div>
  1235. </div>
  1236. </div>
  1237. <p>Even though I haven’t bothered to add the images to the link previews this feels like the right track. This doesn’t work as the final design, of course. But it would work as an initial sketch that can inform initial testing. Specifically, I need to do some testing to see if the conceptual model of the app is something that prospective users can figure out, what terms and labels make sense for them, that sort of thing. There’s nothing worse than testing a sketch or an idea for an UI and then figuring out afterwards that the basic design was never going to work for other reasons.</p>
  1238. <h2>This is not the final design</h2>
  1239. <p>The final design would have to be a lot less rough around the edges but an actual design for implementation needs to answer quite a few questions that this sketch doesn’t:</p>
  1240. <ul>
  1241. <li>Does it reuse the apps other, pre-existing affordances?</li>
  1242. <li>How does drag and drop work, of the card and on the card? How do you indicate draggability? Does a grab handle work?</li>
  1243. <li>Does it match the overall app’s design and UI?</li>
  1244. <li>How much does it lean on OS conventions? Web conventions?</li>
  1245. <li>Does it use rich text, markdown, syntax highlighting?</li>
  1246. <li>How do we animate behaviours and transitions?</li>
  1247. <li>How does it work with keyboard controls?</li>
  1248. <li>What does it sound like to a screen reader?</li>
  1249. <li>How do you distinguish links, cross-references, and attachments? How are they alike? How do they behave differently?</li>
  1250. <li>How are the summary-only cards different? Does having a different style for these cards work? Will it be confusing or break the user’s mental model of what’s happening?</li>
  1251. <li>Does this all make sense and fit together for the intended audience?</li>
  1252. <li>How should the card’s summary reflect the current ‘find’ text?</li>
  1253. </ul>
  1254. <p>And so many more. But this is a first step.</p>
  1255. </article>
  1256. <hr>
  1257. <footer>
  1258. <p>
  1259. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  1260. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  1261. </svg> Accueil</a> •
  1262. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  1263. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  1264. </svg> Suivre</a> •
  1265. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  1266. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  1267. </svg> Pro</a> •
  1268. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  1269. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  1270. </svg> Email</a> •
  1271. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  1272. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  1273. </svg> Légal</abbr>
  1274. </p>
  1275. <template id="theme-selector">
  1276. <form>
  1277. <fieldset>
  1278. <legend><svg class="icon icon-brightness-contrast">
  1279. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  1280. </svg> Thème</legend>
  1281. <label>
  1282. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  1283. </label>
  1284. <label>
  1285. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  1286. </label>
  1287. <label>
  1288. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  1289. </label>
  1290. </fieldset>
  1291. </form>
  1292. </template>
  1293. </footer>
  1294. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  1295. <script>
  1296. function loadThemeForm(templateName) {
  1297. const themeSelectorTemplate = document.querySelector(templateName)
  1298. const form = themeSelectorTemplate.content.firstElementChild
  1299. themeSelectorTemplate.replaceWith(form)
  1300. form.addEventListener('change', (e) => {
  1301. const chosenColorScheme = e.target.value
  1302. localStorage.setItem('theme', chosenColorScheme)
  1303. toggleTheme(chosenColorScheme)
  1304. })
  1305. const selectedTheme = localStorage.getItem('theme')
  1306. if (selectedTheme && selectedTheme !== 'undefined') {
  1307. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  1308. }
  1309. }
  1310. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  1311. window.addEventListener('load', () => {
  1312. let hasDarkRules = false
  1313. for (const styleSheet of Array.from(document.styleSheets)) {
  1314. let mediaRules = []
  1315. for (const cssRule of styleSheet.cssRules) {
  1316. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  1317. continue
  1318. }
  1319. // WARNING: Safari does not have/supports `conditionText`.
  1320. if (cssRule.conditionText) {
  1321. if (cssRule.conditionText !== prefersColorSchemeDark) {
  1322. continue
  1323. }
  1324. } else {
  1325. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  1326. continue
  1327. }
  1328. }
  1329. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  1330. }
  1331. // WARNING: do not try to insert a Rule to a styleSheet you are
  1332. // currently iterating on, otherwise the browser will be stuck
  1333. // in a infinite loop…
  1334. for (const mediaRule of mediaRules) {
  1335. styleSheet.insertRule(mediaRule.cssText)
  1336. hasDarkRules = true
  1337. }
  1338. }
  1339. if (hasDarkRules) {
  1340. loadThemeForm('#theme-selector')
  1341. }
  1342. })
  1343. </script>
  1344. </body>
  1345. </html>