@threadlabs/looma 0.31.0 → 0.31.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.
Files changed (36) hide show
  1. package/components/shared/icons.js +1 -0
  2. package/components/shared/tooltip.js +7 -0
  3. package/components/ui-editor-footnote-editor/ui-editor-footnote-editor.html +57 -0
  4. package/components/ui-editor-footnote-editor/ui-editor-footnote-editor.js +38 -0
  5. package/components/ui-icon/ui-icon.html +1 -0
  6. package/components/ui-tooltip/ui-tooltip.js +4 -1
  7. package/dist/index.js +37 -36
  8. package/editor/chunks/extensions.js +363 -20
  9. package/editor/expressmark.js +1 -1
  10. package/editor/extensions/footnote.d.ts +40 -7
  11. package/editor/extensions/index.d.ts +1 -1
  12. package/editor/extensions/index.js +2 -2
  13. package/editor/footnote-editing.d.ts +46 -0
  14. package/editor/icons.d.ts +1 -0
  15. package/editor/index.js +4 -3
  16. package/editor/table-ui.d.ts +2 -11
  17. package/editor/toolbar-tooltip.d.ts +1 -1
  18. package/package.json +1 -1
  19. package/styles/ui-editor-footnote-editor.css +1 -0
  20. package/vanilla/UiEditorFootnoteEditor.d.ts +30 -0
  21. package/vanilla/UiEditorFootnoteEditor.js +101 -0
  22. package/vanilla/UiIcon.js +1 -1
  23. package/vanilla/index.js +1 -0
  24. package/vue/UiEditorFootnoteEditor.d.ts +57 -0
  25. package/vue/UiEditorFootnoteEditor.js +2 -0
  26. package/vue/UiEditorFootnoteEditor.vue +304 -0
  27. package/vue/UiIcon.vue +4 -0
  28. package/vue/chunks/UiEditorFootnoteEditor.js +306 -0
  29. package/vue/chunks/UiEditorImageToolbar.js +1 -1
  30. package/vue/chunks/UiEditorLinkForm.js +1 -1
  31. package/vue/chunks/UiIcon.js +15 -1
  32. package/vue/components.css +590 -575
  33. package/vue/editor/index.js +643 -45
  34. package/vue/editor/primitives.d.ts +3 -1
  35. package/vue/index.d.ts +1 -0
  36. package/vue/index.js +5 -4
@@ -4243,6 +4243,530 @@ legend.sr-only:where([data-hn-ui-radio-group]) {
4243
4243
  }
4244
4244
  }
4245
4245
 
4246
+ /* This component's hooks style the element they are set on, and never a nested one. */
4247
+ @property --ui-form-field-danger {
4248
+ syntax: "*";
4249
+ inherits: false;
4250
+ }
4251
+ @property --ui-form-field-help-font-size {
4252
+ syntax: "*";
4253
+ inherits: false;
4254
+ }
4255
+ @property --ui-form-field-help-min-block-size {
4256
+ syntax: "*";
4257
+ inherits: false;
4258
+ }
4259
+ @property --ui-form-field-help-text {
4260
+ syntax: "*";
4261
+ inherits: false;
4262
+ }
4263
+ @property --ui-form-field-label-font-size {
4264
+ syntax: "*";
4265
+ inherits: false;
4266
+ }
4267
+ @property --ui-form-field-label-text {
4268
+ syntax: "*";
4269
+ inherits: false;
4270
+ }
4271
+ @property --ui-form-field-min-inline-size {
4272
+ syntax: "*";
4273
+ inherits: false;
4274
+ }
4275
+
4276
+ /* Each named region styles whatever fills it, however it was slotted (slot="…" in HTML, #label
4277
+ in Vue), and generates no box of its own, so an empty region adds no gap. */
4278
+
4279
+ /* A label action, such as a small help button, sits after the label text in a box one line
4280
+ tall, centred on the first line and overflowing it. A button taller than a line of label
4281
+ text then never makes this field's label row taller than its neighbours' in a grid row. */
4282
+
4283
+ /* An error reads in the danger colour, the one the invalid control's border takes. */
4284
+
4285
+ /* A component that sets its display still honours the hidden attribute on its root. */
4286
+ @scope (.ui-form-field) to ([data-hn]) {
4287
+ :scope:where([data-hn-ui-form-field]) {
4288
+ /* Hooks do not inherit, so the parts below read them from the root. */
4289
+ --_ui-form-field-danger: var(--ui-form-field-danger);
4290
+ --_ui-form-field-help-min-block-size: var(--ui-form-field-help-min-block-size);
4291
+ --_ui-form-field-label-text: var(--ui-form-field-label-text);
4292
+ --_ui-form-field-label-font-size: var(--ui-form-field-label-font-size);
4293
+ --_ui-form-field-help-text: var(--ui-form-field-help-text);
4294
+ --_ui-form-field-help-font-size: var(--ui-form-field-help-font-size);
4295
+
4296
+ display: grid;
4297
+ gap: var(--ui-space-2);
4298
+ min-inline-size: var(--ui-form-field-min-inline-size, 0);
4299
+ align-content: start;
4300
+ color: var(--ui-text-primary);
4301
+ }
4302
+ .label:where([data-hn-ui-form-field]),.help:where([data-hn-ui-form-field]),.error:where([data-hn-ui-form-field]) {
4303
+ display: contents;
4304
+ }
4305
+ .label:where([data-hn-ui-form-field]) {
4306
+ color: var(--_ui-form-field-label-text, var(--ui-text-secondary));
4307
+ font-size: var(--_ui-form-field-label-font-size, var(--ui-font-size-sm));
4308
+ }
4309
+ .label:has(> .label-action > *):where([data-hn-ui-form-field]) {
4310
+ display: flex;
4311
+ align-items: start;
4312
+ gap: var(--ui-space-1);
4313
+ }
4314
+ .label-action:where([data-hn-ui-form-field]) {
4315
+ display: flex;
4316
+ align-items: unsafe center;
4317
+ block-size: 1lh;
4318
+ }
4319
+ .label-action:not(:has(> *)):where([data-hn-ui-form-field]) {
4320
+ display: none;
4321
+ }
4322
+ .help:where([data-hn-ui-form-field]) {
4323
+ color: var(--_ui-form-field-help-text, var(--ui-text-muted));
4324
+ font-size: var(--_ui-form-field-help-font-size, var(--ui-font-size-sm));
4325
+ line-height: var(--ui-line-height-md);
4326
+ }
4327
+ .help > :where(:not([data-hn-ui-form-field])):is(*) {
4328
+ min-block-size: var(--_ui-form-field-help-min-block-size, 0);
4329
+ }
4330
+ .error:where([data-hn-ui-form-field]) {
4331
+ display: none;
4332
+ }
4333
+ :scope[data-ui-form-field-state~="invalid"] .error:where([data-hn-ui-form-field]) {
4334
+ display: contents;
4335
+ }
4336
+ .error:where([data-hn-ui-form-field]),:where(:not([data-hn-ui-form-field])):is([role="alert"]) {
4337
+ color: var(--_ui-form-field-danger, var(--ui-field-danger, var(--ui-danger)));
4338
+ font-size: var(--ui-font-size-sm);
4339
+ }
4340
+ :scope[hidden]:where([data-hn-ui-form-field]) {
4341
+ display: none;
4342
+ }
4343
+ }
4344
+
4345
+ @property --ui-input-group-radius {
4346
+ syntax: "*";
4347
+ inherits: false;
4348
+ }
4349
+ @property --ui-input-group-invalid-border {
4350
+ syntax: "*";
4351
+ inherits: false;
4352
+ }
4353
+ @property --ui-input-group-invalid-focus-shadow {
4354
+ syntax: "*";
4355
+ inherits: false;
4356
+ }
4357
+ /* An input with fixed text before or after it, such as a domain after a site's name
4358
+ ("acme" + ".example.com") or slashes around a path. The group draws the field's border and
4359
+ focus ring around the input and its affixes together; the input inside drops its own through
4360
+ its custom properties, so it is still the one ui-input, with its form behaviour intact. The
4361
+ affixes are plain text beside the input, so name the input with a label that says what the
4362
+ whole value is. The action slot takes one button at the end, inside the border, for the one
4363
+ thing the field is for ("Continue" after a site's address), so it reads as the field's action
4364
+ and not the form's. */
4365
+
4366
+ /* The frame takes its input's size, so a small group lines up with small buttons and fields. */
4367
+
4368
+ /* The ring shows when the field's input would show its own: when it is focus-visible, which a
4369
+ text input is for a pointer as well as a keyboard. A focused action button draws its own. */
4370
+
4371
+ /* Forced colors drop box shadows, so the focus ring becomes an outline. */
4372
+
4373
+ /* Fixed text is part of the value, but should not look editable. */
4374
+
4375
+ /* Centred rather than padded above and below: the group is exactly a lone Input's height, so a
4376
+ small button sits inside it with the difference split evenly, and the field keeps its height. */
4377
+
4378
+ /* Multiline text owns the first row; its action remains inside the same frame below it. */
4379
+
4380
+ /* The same textarea keeps its full resizeable box. Its text reserves the action's live width,
4381
+ so a translated label or pending indicator cannot cover what the person types. */
4382
+
4383
+ /* A component that sets its display still honours the hidden attribute on its root. */
4384
+ @scope (.ui-input-group) to ([data-hn]) {
4385
+ :scope:where([data-hn-ui-input-group]) {
4386
+ /* The group's own border and height stand for the input's, so the group is a lone Input's height.
4387
+ Hooks do not inherit, so these are defaults for the input; its own hooks still win. */
4388
+ --_ui-default-input-border-width: 0;
4389
+ --_ui-default-input-min-block-size: 0;
4390
+ --_ui-default-input-border: transparent;
4391
+ --_ui-default-input-border-hover: transparent;
4392
+ --_ui-default-input-focus-border: transparent;
4393
+ --_ui-default-input-invalid-border: transparent;
4394
+ --_ui-default-input-invalid-focus-shadow: none;
4395
+ --_ui-default-input-shadow: none;
4396
+ --_ui-default-input-focus-shadow: none;
4397
+ --_ui-default-input-focus-outline: none;
4398
+ --_ui-default-input-radius: 0;
4399
+ --_ui-default-input-surface: transparent;
4400
+ --_ui-default-textarea-border-width: 0;
4401
+ --_ui-default-textarea-border: transparent;
4402
+ --_ui-default-textarea-border-hover: transparent;
4403
+ --_ui-default-textarea-focus-border: transparent;
4404
+ --_ui-default-textarea-invalid-border: transparent;
4405
+ --_ui-default-textarea-invalid-focus-shadow: none;
4406
+ --_ui-default-textarea-shadow: none;
4407
+ --_ui-default-textarea-focus-shadow: none;
4408
+ --_ui-default-textarea-focus-outline: none;
4409
+ --_ui-default-textarea-radius: 0;
4410
+ --_ui-default-textarea-surface: transparent;
4411
+
4412
+ display: flex;
4413
+ align-items: stretch;
4414
+ box-sizing: border-box;
4415
+ inline-size: 100%;
4416
+ min-inline-size: 0;
4417
+ min-block-size: var(--ui-control-size-md);
4418
+ /* The frame reads the shared control tokens with a lone Input's fallbacks, so the two match. */
4419
+ border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
4420
+ border-radius: var(--ui-input-group-radius, var(--ui-field-radius, var(--ui-radius-md)));
4421
+ background: var(--ui-control-surface, var(--ui-surface-elevated));
4422
+ box-shadow: var(--ui-control-inset);
4423
+ overflow: hidden;
4424
+ /* A press anywhere in the frame focuses the input. */
4425
+ cursor: text;
4426
+ transition:
4427
+ border-color var(--ui-motion-fast) var(--ui-motion-ease),
4428
+ box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
4429
+ }
4430
+ :scope[data-ui-input-group-state~="variant=subtle"]:where([data-hn-ui-input-group]) {
4431
+ border-color: var(--ui-border);
4432
+ background: var(--ui-surface-muted);
4433
+ box-shadow: none;
4434
+ }
4435
+ :scope:has(.field [data-ui-input-state~="size=sm"]):where([data-hn-ui-input-group]) {
4436
+ min-block-size: var(--ui-control-size-sm);
4437
+ }
4438
+ :scope:has(.field [data-ui-input-state~="size=lg"]):where([data-hn-ui-input-group]) {
4439
+ min-block-size: var(--ui-control-size-lg);
4440
+ }
4441
+ @media (pointer: coarse) {
4442
+ :scope:where([data-hn-ui-input-group]) {
4443
+ min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
4444
+ }
4445
+ :scope:has(.field [data-ui-input-state~="size=sm"]):where([data-hn-ui-input-group]) {
4446
+ min-block-size: var(--ui-control-min-block-size);
4447
+ }
4448
+ }
4449
+ :scope:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))):where([data-hn-ui-input-group]) {
4450
+ border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
4451
+ }
4452
+ :scope:has(.field :is(input, textarea):focus-visible):where([data-hn-ui-input-group]) {
4453
+ border-color: var(--ui-control-focus, var(--ui-accent-solid));
4454
+ box-shadow:
4455
+ var(--ui-control-inset),
4456
+ var(--ui-control-focus-halo-shadow);
4457
+ }
4458
+ :scope:has([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):where([data-hn-ui-input-group]) {
4459
+ border-color: var(
4460
+ --ui-input-group-invalid-border,
4461
+ var(--ui-field-danger, var(--ui-danger-solid))
4462
+ );
4463
+ }
4464
+ :scope:has(:is(input, textarea):is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible):where([data-hn-ui-input-group]) {
4465
+ box-shadow: var(
4466
+ --ui-input-group-invalid-focus-shadow,
4467
+ var(--ui-control-inset),
4468
+ 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
4469
+ in srgb,
4470
+ var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
4471
+ transparent
4472
+ )
4473
+ );
4474
+ }
4475
+ @media (forced-colors: active) {
4476
+ :scope:has(:is(input, textarea):focus-visible):where([data-hn-ui-input-group]) {
4477
+ outline: var(--ui-focus-width) solid Highlight;
4478
+ outline-offset: var(--ui-border-width);
4479
+ }
4480
+ }
4481
+ :scope[data-ui-input-group-state~="disabled"]:where([data-hn-ui-input-group]),:scope:has(:is(input, textarea):disabled):where([data-hn-ui-input-group]) {
4482
+ background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
4483
+ box-shadow: none;
4484
+ cursor: not-allowed;
4485
+ }
4486
+ .field:where([data-hn-ui-input-group]) {
4487
+ display: flex;
4488
+ flex: 1;
4489
+ min-inline-size: 0;
4490
+ }
4491
+ .affix:where([data-hn-ui-input-group]) {
4492
+ display: inline-flex;
4493
+ flex: none;
4494
+ align-items: center;
4495
+ padding-inline: var(--ui-space-2);
4496
+ color: var(--ui-text-muted);
4497
+ font-family: var(--ui-font-family-mono);
4498
+ font-size: var(--ui-font-size-sm);
4499
+ white-space: nowrap;
4500
+ }
4501
+ .affix:has([slot="prefix"]):where([data-hn-ui-input-group]),.affix:has([slot="suffix"]):where([data-hn-ui-input-group]) {
4502
+ background: var(--ui-surface-muted);
4503
+ }
4504
+ .affix:has([slot="prefix"]):where([data-hn-ui-input-group]) {
4505
+ border-inline-end: var(--ui-border-width) solid var(--ui-border-divider);
4506
+ }
4507
+ .affix:has([slot="suffix"]):where([data-hn-ui-input-group]) {
4508
+ border-inline-start: var(--ui-border-width) solid var(--ui-border-divider);
4509
+ }
4510
+ :scope[data-ui-input-group-state~="prefixKind=icon"]:where([data-hn-ui-input-group]) {
4511
+ --_ui-default-input-padding-inline-start: var(--ui-space-2);
4512
+ }
4513
+ :scope[data-ui-input-group-state~="prefixKind=icon"]
4514
+ .affix:first-child:where([data-hn-ui-input-group]) {
4515
+ padding-inline-end: 0;
4516
+ border-inline-end: 0;
4517
+ background: transparent;
4518
+ }
4519
+ .action:where([data-hn-ui-input-group]) {
4520
+ display: flex;
4521
+ flex: none;
4522
+ align-items: center;
4523
+ padding-inline: var(--ui-space-1);
4524
+ }
4525
+ :scope[data-ui-input-group-state~="multiline"]:where([data-hn-ui-input-group]) {
4526
+ display: grid;
4527
+ grid-template-columns: auto minmax(0, 1fr) auto;
4528
+ }
4529
+ :scope[data-ui-input-group-state~="multiline"] .field:where([data-hn-ui-input-group]) {
4530
+ grid-column: 2;
4531
+ }
4532
+ :scope[data-ui-input-group-state~="multiline"] .action:where([data-hn-ui-input-group]) {
4533
+ grid-column: 1 / -1;
4534
+ justify-content: end;
4535
+ padding: var(--ui-space-2);
4536
+ padding-block-start: 0;
4537
+ }
4538
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]:where([data-hn-ui-input-group]) {
4539
+ --_ui-default-textarea-padding-inline-end: calc(
4540
+ var(--_ui-input-group-action-inline-size, 7rem) + var(--ui-space-3)
4541
+ );
4542
+ }
4543
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
4544
+ .field:where([data-hn-ui-input-group]) {
4545
+ grid-row: 1;
4546
+ }
4547
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
4548
+ .action:where([data-hn-ui-input-group]) {
4549
+ grid-column: 2;
4550
+ grid-row: 1;
4551
+ align-self: start;
4552
+ justify-self: end;
4553
+ padding: var(--ui-space-2);
4554
+ }
4555
+ .affix:empty:where([data-hn-ui-input-group]),.action:empty:where([data-hn-ui-input-group]) {
4556
+ display: none;
4557
+ }
4558
+ :scope[hidden]:where([data-hn-ui-input-group]) {
4559
+ display: none;
4560
+ }
4561
+ }
4562
+
4563
+ /* This component's hooks style the element they are set on, and never a nested one. */
4564
+ @property --ui-textarea-border-width {
4565
+ syntax: "*";
4566
+ inherits: false;
4567
+ }
4568
+ @property --ui-textarea-border {
4569
+ syntax: "*";
4570
+ inherits: false;
4571
+ }
4572
+ @property --ui-textarea-border-hover {
4573
+ syntax: "*";
4574
+ inherits: false;
4575
+ }
4576
+ @property --ui-textarea-focus-border {
4577
+ syntax: "*";
4578
+ inherits: false;
4579
+ }
4580
+ @property --ui-textarea-focus-outline {
4581
+ syntax: "*";
4582
+ inherits: false;
4583
+ }
4584
+ @property --ui-textarea-surface {
4585
+ syntax: "*";
4586
+ inherits: false;
4587
+ }
4588
+ @property --ui-textarea-focus-shadow {
4589
+ syntax: "*";
4590
+ inherits: false;
4591
+ }
4592
+ @property --ui-textarea-invalid-border {
4593
+ syntax: "*";
4594
+ inherits: false;
4595
+ }
4596
+ @property --ui-textarea-invalid-focus-shadow {
4597
+ syntax: "*";
4598
+ inherits: false;
4599
+ }
4600
+ @property --ui-textarea-radius {
4601
+ syntax: "*";
4602
+ inherits: false;
4603
+ }
4604
+ @property --ui-textarea-shadow {
4605
+ syntax: "*";
4606
+ inherits: false;
4607
+ }
4608
+ @property --ui-textarea-padding-inline-end {
4609
+ syntax: "*";
4610
+ inherits: false;
4611
+ }
4612
+
4613
+ /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
4614
+
4615
+ /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
4616
+
4617
+ /* Below 16px, iOS zooms the page into the focused field, so touch keeps body-size text. */
4618
+
4619
+ /* A component that sets its display still honours the hidden attribute on its root. */
4620
+ @scope (.ui-textarea) to ([data-hn]) {
4621
+ :scope {
4622
+ box-sizing: border-box;
4623
+ display: inline-block;
4624
+ inline-size: 100%;
4625
+ min-inline-size: 0;
4626
+ min-block-size: calc(3rem * 2);
4627
+ margin: 0;
4628
+ --_padding-block: var(--ui-space-2);
4629
+ padding: var(--ui-space-2) var(--ui-space-3);
4630
+ padding-inline-end: var(
4631
+ --ui-textarea-padding-inline-end,
4632
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-3))
4633
+ );
4634
+ appearance: none;
4635
+ resize: vertical;
4636
+ border: var(
4637
+ --ui-textarea-border-width,
4638
+ var(--_ui-default-textarea-border-width, var(--ui-border-width))
4639
+ ) solid
4640
+ var(
4641
+ --ui-textarea-border,
4642
+ var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong)))
4643
+ );
4644
+ border-radius: var(
4645
+ --ui-textarea-radius,
4646
+ var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)))
4647
+ );
4648
+ background: var(
4649
+ --ui-textarea-surface,
4650
+ var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated)))
4651
+ );
4652
+ color: var(--ui-text-primary);
4653
+ font: inherit;
4654
+ line-height: var(--ui-line-height-md);
4655
+ box-shadow: var(
4656
+ --ui-textarea-shadow,
4657
+ var(--_ui-default-textarea-shadow, var(--ui-control-inset))
4658
+ );
4659
+ transition:
4660
+ border-color var(--ui-motion-fast) var(--ui-motion-ease),
4661
+ box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
4662
+ background-color var(--ui-motion-fast) var(--ui-motion-ease);
4663
+ }
4664
+ :scope::selection {
4665
+ background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
4666
+ color: inherit;
4667
+ }
4668
+ :scope::placeholder {
4669
+ color: var(--ui-control-placeholder, var(--ui-text-secondary));
4670
+ opacity: 1;
4671
+ }
4672
+ :scope:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
4673
+ border-color: var(
4674
+ --ui-textarea-border-hover,
4675
+ var(
4676
+ --_ui-default-textarea-border-hover,
4677
+ var(--ui-control-border-hover, var(--ui-text-secondary))
4678
+ )
4679
+ );
4680
+ }
4681
+ :scope:focus-visible {
4682
+ border-color: var(
4683
+ --ui-textarea-focus-border,
4684
+ var(--_ui-default-textarea-focus-border, var(--ui-control-focus, var(--ui-accent-solid)))
4685
+ );
4686
+ outline: var(--ui-textarea-focus-outline, var(--_ui-default-textarea-focus-outline, none));
4687
+ box-shadow: var(
4688
+ --ui-textarea-focus-shadow,
4689
+ var(
4690
+ --_ui-default-textarea-focus-shadow,
4691
+ var(--ui-control-inset),
4692
+ var(--ui-control-focus-halo-shadow)
4693
+ )
4694
+ );
4695
+ }
4696
+ :scope[aria-invalid="true"],:scope:is(:user-invalid, [data-user-invalid]) {
4697
+ border-color: var(
4698
+ --ui-textarea-invalid-border,
4699
+ var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))
4700
+ );
4701
+ }
4702
+ :scope:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
4703
+ box-shadow: var(
4704
+ --ui-textarea-invalid-focus-shadow,
4705
+ var(
4706
+ --_ui-default-textarea-invalid-focus-shadow,
4707
+ var(--ui-control-inset),
4708
+ 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
4709
+ in srgb,
4710
+ var(
4711
+ --ui-textarea-invalid-border,
4712
+ var(
4713
+ --_ui-default-textarea-invalid-border,
4714
+ var(--ui-field-danger, var(--ui-danger-solid))
4715
+ )
4716
+ ) 22%,
4717
+ transparent
4718
+ )
4719
+ )
4720
+ );
4721
+ }
4722
+ :scope:disabled {
4723
+ background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
4724
+ color: var(--ui-disabled-text);
4725
+ box-shadow: none;
4726
+ cursor: not-allowed;
4727
+ }
4728
+ :scope[data-ui-textarea-state~="size=sm"] {
4729
+ --_padding-block: var(--ui-space-1);
4730
+ padding: var(--ui-space-1) var(--ui-space-2);
4731
+ padding-inline-end: var(
4732
+ --ui-textarea-padding-inline-end,
4733
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-2))
4734
+ );
4735
+ font-size: var(--ui-font-size-sm);
4736
+ }
4737
+ :scope[data-ui-textarea-state~="size=lg"] {
4738
+ --_padding-block: var(--ui-space-3);
4739
+ padding: var(--ui-space-3) var(--ui-space-4);
4740
+ padding-inline-end: var(
4741
+ --ui-textarea-padding-inline-end,
4742
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-4))
4743
+ );
4744
+ font-size: var(--ui-font-size-md);
4745
+ }
4746
+ :scope[data-ui-textarea-state~="maxRows"] {
4747
+ max-block-size: calc(
4748
+ max(var(--_rows), var(--_max-rows)) * 1lh + var(--_padding-block) + var(--_padding-block) +
4749
+ 2px
4750
+ );
4751
+ }
4752
+ @supports (field-sizing: content) {
4753
+ :scope[data-ui-textarea-state~="autosize"] {
4754
+ field-sizing: content;
4755
+ min-block-size: calc(
4756
+ var(--_rows) * 1lh + var(--_padding-block) + var(--_padding-block) + 2px
4757
+ );
4758
+ }
4759
+ }
4760
+ @media (pointer: coarse) {
4761
+ :scope[data-ui-textarea-state~="size=sm"] {
4762
+ font-size: inherit;
4763
+ }
4764
+ }
4765
+ :scope[hidden] {
4766
+ display: none;
4767
+ }
4768
+ }
4769
+
4246
4770
  /* One row, always. When the controls outgrow the space, the row scrolls sideways rather than
4247
4771
  wrapping into several lines, and a fade at each edge that has more to show says so. */
4248
4772
 
@@ -4349,144 +4873,45 @@ legend.sr-only:where([data-hn-ui-radio-group]) {
4349
4873
  border-top: var(--ui-border-width) solid
4350
4874
  var(--ui-editor-toolbar-border, var(--ui-border-divider));
4351
4875
  border-bottom: none;
4352
- box-shadow: var(--ui-editor-toolbar-mobile-shadow, 0 -2px 8px rgb(0 0 0 / 0.06));
4353
- }
4354
- :scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar]) {
4355
- position: relative;
4356
- inset: auto;
4357
- min-height: auto;
4358
- border: var(--ui-border-width) solid var(--ui-editor-toolbar-border, var(--ui-border-strong));
4359
- border-radius: var(--ui-editor-toolbar-floating-radius, var(--ui-radius-lg));
4360
- box-shadow: var(
4361
- --ui-editor-toolbar-floating-shadow,
4362
- var(--ui-shadow-lg, 0 10px 24px rgb(0 0 0 / 0.12))
4363
- );
4364
- }
4365
- }
4366
- .looma-editor__sticky-toolbar-shell :scope:where([data-hn-ui-editor-toolbar]) {
4367
- position: relative;
4368
- min-height: var(--ui-editor-toolbar-min-height, 48px);
4369
- background: transparent;
4370
- border: 0;
4371
- box-shadow: none;
4372
- }
4373
- .looma-editor__mobile-toolbar-shell
4374
- :scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar]) {
4375
- width: 100%;
4376
- max-width: none;
4377
- min-height: 100%;
4378
- padding: 0;
4379
- background: transparent;
4380
- border: 0;
4381
- border-radius: 0;
4382
- box-shadow: none;
4383
- backdrop-filter: none;
4384
- }
4385
- .looma-editor__mobile-toolbar-shell
4386
- :scope[data-ui-editor-toolbar-state~="floating"]
4387
- .strip:where([data-hn-ui-editor-toolbar]) {
4388
- scroll-snap-type: x mandatory;
4389
- }
4390
- :scope[hidden]:where([data-hn-ui-editor-toolbar]) {
4391
- display: none;
4392
- }
4393
- }
4394
-
4395
- /* This component's hooks style the element they are set on, and never a nested one. */
4396
- @property --ui-form-field-danger {
4397
- syntax: "*";
4398
- inherits: false;
4399
- }
4400
- @property --ui-form-field-help-font-size {
4401
- syntax: "*";
4402
- inherits: false;
4403
- }
4404
- @property --ui-form-field-help-min-block-size {
4405
- syntax: "*";
4406
- inherits: false;
4407
- }
4408
- @property --ui-form-field-help-text {
4409
- syntax: "*";
4410
- inherits: false;
4411
- }
4412
- @property --ui-form-field-label-font-size {
4413
- syntax: "*";
4414
- inherits: false;
4415
- }
4416
- @property --ui-form-field-label-text {
4417
- syntax: "*";
4418
- inherits: false;
4419
- }
4420
- @property --ui-form-field-min-inline-size {
4421
- syntax: "*";
4422
- inherits: false;
4423
- }
4424
-
4425
- /* Each named region styles whatever fills it, however it was slotted (slot="…" in HTML, #label
4426
- in Vue), and generates no box of its own, so an empty region adds no gap. */
4427
-
4428
- /* A label action, such as a small help button, sits after the label text in a box one line
4429
- tall, centred on the first line and overflowing it. A button taller than a line of label
4430
- text then never makes this field's label row taller than its neighbours' in a grid row. */
4431
-
4432
- /* An error reads in the danger colour, the one the invalid control's border takes. */
4433
-
4434
- /* A component that sets its display still honours the hidden attribute on its root. */
4435
- @scope (.ui-form-field) to ([data-hn]) {
4436
- :scope:where([data-hn-ui-form-field]) {
4437
- /* Hooks do not inherit, so the parts below read them from the root. */
4438
- --_ui-form-field-danger: var(--ui-form-field-danger);
4439
- --_ui-form-field-help-min-block-size: var(--ui-form-field-help-min-block-size);
4440
- --_ui-form-field-label-text: var(--ui-form-field-label-text);
4441
- --_ui-form-field-label-font-size: var(--ui-form-field-label-font-size);
4442
- --_ui-form-field-help-text: var(--ui-form-field-help-text);
4443
- --_ui-form-field-help-font-size: var(--ui-form-field-help-font-size);
4444
-
4445
- display: grid;
4446
- gap: var(--ui-space-2);
4447
- min-inline-size: var(--ui-form-field-min-inline-size, 0);
4448
- align-content: start;
4449
- color: var(--ui-text-primary);
4450
- }
4451
- .label:where([data-hn-ui-form-field]),.help:where([data-hn-ui-form-field]),.error:where([data-hn-ui-form-field]) {
4452
- display: contents;
4453
- }
4454
- .label:where([data-hn-ui-form-field]) {
4455
- color: var(--_ui-form-field-label-text, var(--ui-text-secondary));
4456
- font-size: var(--_ui-form-field-label-font-size, var(--ui-font-size-sm));
4457
- }
4458
- .label:has(> .label-action > *):where([data-hn-ui-form-field]) {
4459
- display: flex;
4460
- align-items: start;
4461
- gap: var(--ui-space-1);
4462
- }
4463
- .label-action:where([data-hn-ui-form-field]) {
4464
- display: flex;
4465
- align-items: unsafe center;
4466
- block-size: 1lh;
4467
- }
4468
- .label-action:not(:has(> *)):where([data-hn-ui-form-field]) {
4469
- display: none;
4876
+ box-shadow: var(--ui-editor-toolbar-mobile-shadow, 0 -2px 8px rgb(0 0 0 / 0.06));
4470
4877
  }
4471
- .help:where([data-hn-ui-form-field]) {
4472
- color: var(--_ui-form-field-help-text, var(--ui-text-muted));
4473
- font-size: var(--_ui-form-field-help-font-size, var(--ui-font-size-sm));
4474
- line-height: var(--ui-line-height-md);
4878
+ :scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar]) {
4879
+ position: relative;
4880
+ inset: auto;
4881
+ min-height: auto;
4882
+ border: var(--ui-border-width) solid var(--ui-editor-toolbar-border, var(--ui-border-strong));
4883
+ border-radius: var(--ui-editor-toolbar-floating-radius, var(--ui-radius-lg));
4884
+ box-shadow: var(
4885
+ --ui-editor-toolbar-floating-shadow,
4886
+ var(--ui-shadow-lg, 0 10px 24px rgb(0 0 0 / 0.12))
4887
+ );
4475
4888
  }
4476
- .help > :where(:not([data-hn-ui-form-field])):is(*) {
4477
- min-block-size: var(--_ui-form-field-help-min-block-size, 0);
4478
4889
  }
4479
- .error:where([data-hn-ui-form-field]) {
4480
- display: none;
4890
+ .looma-editor__sticky-toolbar-shell :scope:where([data-hn-ui-editor-toolbar]) {
4891
+ position: relative;
4892
+ min-height: var(--ui-editor-toolbar-min-height, 48px);
4893
+ background: transparent;
4894
+ border: 0;
4895
+ box-shadow: none;
4481
4896
  }
4482
- :scope[data-ui-form-field-state~="invalid"] .error:where([data-hn-ui-form-field]) {
4483
- display: contents;
4897
+ .looma-editor__mobile-toolbar-shell
4898
+ :scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar]) {
4899
+ width: 100%;
4900
+ max-width: none;
4901
+ min-height: 100%;
4902
+ padding: 0;
4903
+ background: transparent;
4904
+ border: 0;
4905
+ border-radius: 0;
4906
+ box-shadow: none;
4907
+ backdrop-filter: none;
4484
4908
  }
4485
- .error:where([data-hn-ui-form-field]),:where(:not([data-hn-ui-form-field])):is([role="alert"]) {
4486
- color: var(--_ui-form-field-danger, var(--ui-field-danger, var(--ui-danger)));
4487
- font-size: var(--ui-font-size-sm);
4909
+ .looma-editor__mobile-toolbar-shell
4910
+ :scope[data-ui-editor-toolbar-state~="floating"]
4911
+ .strip:where([data-hn-ui-editor-toolbar]) {
4912
+ scroll-snap-type: x mandatory;
4488
4913
  }
4489
- :scope[hidden]:where([data-hn-ui-form-field]) {
4914
+ :scope[hidden]:where([data-hn-ui-editor-toolbar]) {
4490
4915
  display: none;
4491
4916
  }
4492
4917
  }
@@ -5237,237 +5662,19 @@ html[data-ui-input-modality="touch"] :scope:where([data-hn-ui-icon-button])::aft
5237
5662
  @media (pointer: coarse) {
5238
5663
  .grid {
5239
5664
  grid-template-columns: repeat(var(--_cols, 8), 44px);
5240
- grid-template-rows: repeat(var(--_rows, 8), 44px);
5241
- }
5242
- .cell {
5243
- width: 44px;
5244
- height: 44px;
5245
- }
5246
- }
5247
- @media (forced-colors: active) {
5248
- .cell:focus-visible {
5249
- outline-color: Highlight;
5250
- }
5251
- }
5252
- :scope[hidden] {
5253
- display: none;
5254
- }
5255
- }
5256
-
5257
- @property --ui-input-group-radius {
5258
- syntax: "*";
5259
- inherits: false;
5260
- }
5261
- @property --ui-input-group-invalid-border {
5262
- syntax: "*";
5263
- inherits: false;
5264
- }
5265
- @property --ui-input-group-invalid-focus-shadow {
5266
- syntax: "*";
5267
- inherits: false;
5268
- }
5269
- /* An input with fixed text before or after it, such as a domain after a site's name
5270
- ("acme" + ".example.com") or slashes around a path. The group draws the field's border and
5271
- focus ring around the input and its affixes together; the input inside drops its own through
5272
- its custom properties, so it is still the one ui-input, with its form behaviour intact. The
5273
- affixes are plain text beside the input, so name the input with a label that says what the
5274
- whole value is. The action slot takes one button at the end, inside the border, for the one
5275
- thing the field is for ("Continue" after a site's address), so it reads as the field's action
5276
- and not the form's. */
5277
-
5278
- /* The frame takes its input's size, so a small group lines up with small buttons and fields. */
5279
-
5280
- /* The ring shows when the field's input would show its own: when it is focus-visible, which a
5281
- text input is for a pointer as well as a keyboard. A focused action button draws its own. */
5282
-
5283
- /* Forced colors drop box shadows, so the focus ring becomes an outline. */
5284
-
5285
- /* Fixed text is part of the value, but should not look editable. */
5286
-
5287
- /* Centred rather than padded above and below: the group is exactly a lone Input's height, so a
5288
- small button sits inside it with the difference split evenly, and the field keeps its height. */
5289
-
5290
- /* Multiline text owns the first row; its action remains inside the same frame below it. */
5291
-
5292
- /* The same textarea keeps its full resizeable box. Its text reserves the action's live width,
5293
- so a translated label or pending indicator cannot cover what the person types. */
5294
-
5295
- /* A component that sets its display still honours the hidden attribute on its root. */
5296
- @scope (.ui-input-group) to ([data-hn]) {
5297
- :scope:where([data-hn-ui-input-group]) {
5298
- /* The group's own border and height stand for the input's, so the group is a lone Input's height.
5299
- Hooks do not inherit, so these are defaults for the input; its own hooks still win. */
5300
- --_ui-default-input-border-width: 0;
5301
- --_ui-default-input-min-block-size: 0;
5302
- --_ui-default-input-border: transparent;
5303
- --_ui-default-input-border-hover: transparent;
5304
- --_ui-default-input-focus-border: transparent;
5305
- --_ui-default-input-invalid-border: transparent;
5306
- --_ui-default-input-invalid-focus-shadow: none;
5307
- --_ui-default-input-shadow: none;
5308
- --_ui-default-input-focus-shadow: none;
5309
- --_ui-default-input-focus-outline: none;
5310
- --_ui-default-input-radius: 0;
5311
- --_ui-default-input-surface: transparent;
5312
- --_ui-default-textarea-border-width: 0;
5313
- --_ui-default-textarea-border: transparent;
5314
- --_ui-default-textarea-border-hover: transparent;
5315
- --_ui-default-textarea-focus-border: transparent;
5316
- --_ui-default-textarea-invalid-border: transparent;
5317
- --_ui-default-textarea-invalid-focus-shadow: none;
5318
- --_ui-default-textarea-shadow: none;
5319
- --_ui-default-textarea-focus-shadow: none;
5320
- --_ui-default-textarea-focus-outline: none;
5321
- --_ui-default-textarea-radius: 0;
5322
- --_ui-default-textarea-surface: transparent;
5323
-
5324
- display: flex;
5325
- align-items: stretch;
5326
- box-sizing: border-box;
5327
- inline-size: 100%;
5328
- min-inline-size: 0;
5329
- min-block-size: var(--ui-control-size-md);
5330
- /* The frame reads the shared control tokens with a lone Input's fallbacks, so the two match. */
5331
- border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
5332
- border-radius: var(--ui-input-group-radius, var(--ui-field-radius, var(--ui-radius-md)));
5333
- background: var(--ui-control-surface, var(--ui-surface-elevated));
5334
- box-shadow: var(--ui-control-inset);
5335
- overflow: hidden;
5336
- /* A press anywhere in the frame focuses the input. */
5337
- cursor: text;
5338
- transition:
5339
- border-color var(--ui-motion-fast) var(--ui-motion-ease),
5340
- box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
5341
- }
5342
- :scope[data-ui-input-group-state~="variant=subtle"]:where([data-hn-ui-input-group]) {
5343
- border-color: var(--ui-border);
5344
- background: var(--ui-surface-muted);
5345
- box-shadow: none;
5346
- }
5347
- :scope:has(.field [data-ui-input-state~="size=sm"]):where([data-hn-ui-input-group]) {
5348
- min-block-size: var(--ui-control-size-sm);
5349
- }
5350
- :scope:has(.field [data-ui-input-state~="size=lg"]):where([data-hn-ui-input-group]) {
5351
- min-block-size: var(--ui-control-size-lg);
5352
- }
5353
- @media (pointer: coarse) {
5354
- :scope:where([data-hn-ui-input-group]) {
5355
- min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
5356
- }
5357
- :scope:has(.field [data-ui-input-state~="size=sm"]):where([data-hn-ui-input-group]) {
5358
- min-block-size: var(--ui-control-min-block-size);
5359
- }
5360
- }
5361
- :scope:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))):where([data-hn-ui-input-group]) {
5362
- border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
5363
- }
5364
- :scope:has(.field :is(input, textarea):focus-visible):where([data-hn-ui-input-group]) {
5365
- border-color: var(--ui-control-focus, var(--ui-accent-solid));
5366
- box-shadow:
5367
- var(--ui-control-inset),
5368
- var(--ui-control-focus-halo-shadow);
5369
- }
5370
- :scope:has([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):where([data-hn-ui-input-group]) {
5371
- border-color: var(
5372
- --ui-input-group-invalid-border,
5373
- var(--ui-field-danger, var(--ui-danger-solid))
5374
- );
5375
- }
5376
- :scope:has(:is(input, textarea):is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible):where([data-hn-ui-input-group]) {
5377
- box-shadow: var(
5378
- --ui-input-group-invalid-focus-shadow,
5379
- var(--ui-control-inset),
5380
- 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
5381
- in srgb,
5382
- var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
5383
- transparent
5384
- )
5385
- );
5386
- }
5387
- @media (forced-colors: active) {
5388
- :scope:has(:is(input, textarea):focus-visible):where([data-hn-ui-input-group]) {
5389
- outline: var(--ui-focus-width) solid Highlight;
5390
- outline-offset: var(--ui-border-width);
5391
- }
5392
- }
5393
- :scope[data-ui-input-group-state~="disabled"]:where([data-hn-ui-input-group]),:scope:has(:is(input, textarea):disabled):where([data-hn-ui-input-group]) {
5394
- background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
5395
- box-shadow: none;
5396
- cursor: not-allowed;
5397
- }
5398
- .field:where([data-hn-ui-input-group]) {
5399
- display: flex;
5400
- flex: 1;
5401
- min-inline-size: 0;
5402
- }
5403
- .affix:where([data-hn-ui-input-group]) {
5404
- display: inline-flex;
5405
- flex: none;
5406
- align-items: center;
5407
- padding-inline: var(--ui-space-2);
5408
- color: var(--ui-text-muted);
5409
- font-family: var(--ui-font-family-mono);
5410
- font-size: var(--ui-font-size-sm);
5411
- white-space: nowrap;
5412
- }
5413
- .affix:has([slot="prefix"]):where([data-hn-ui-input-group]),.affix:has([slot="suffix"]):where([data-hn-ui-input-group]) {
5414
- background: var(--ui-surface-muted);
5415
- }
5416
- .affix:has([slot="prefix"]):where([data-hn-ui-input-group]) {
5417
- border-inline-end: var(--ui-border-width) solid var(--ui-border-divider);
5418
- }
5419
- .affix:has([slot="suffix"]):where([data-hn-ui-input-group]) {
5420
- border-inline-start: var(--ui-border-width) solid var(--ui-border-divider);
5421
- }
5422
- :scope[data-ui-input-group-state~="prefixKind=icon"]:where([data-hn-ui-input-group]) {
5423
- --_ui-default-input-padding-inline-start: var(--ui-space-2);
5424
- }
5425
- :scope[data-ui-input-group-state~="prefixKind=icon"]
5426
- .affix:first-child:where([data-hn-ui-input-group]) {
5427
- padding-inline-end: 0;
5428
- border-inline-end: 0;
5429
- background: transparent;
5430
- }
5431
- .action:where([data-hn-ui-input-group]) {
5432
- display: flex;
5433
- flex: none;
5434
- align-items: center;
5435
- padding-inline: var(--ui-space-1);
5436
- }
5437
- :scope[data-ui-input-group-state~="multiline"]:where([data-hn-ui-input-group]) {
5438
- display: grid;
5439
- grid-template-columns: auto minmax(0, 1fr) auto;
5440
- }
5441
- :scope[data-ui-input-group-state~="multiline"] .field:where([data-hn-ui-input-group]) {
5442
- grid-column: 2;
5443
- }
5444
- :scope[data-ui-input-group-state~="multiline"] .action:where([data-hn-ui-input-group]) {
5445
- grid-column: 1 / -1;
5446
- justify-content: end;
5447
- padding: var(--ui-space-2);
5448
- padding-block-start: 0;
5665
+ grid-template-rows: repeat(var(--_rows, 8), 44px);
5449
5666
  }
5450
- :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]:where([data-hn-ui-input-group]) {
5451
- --_ui-default-textarea-padding-inline-end: calc(
5452
- var(--_ui-input-group-action-inline-size, 7rem) + var(--ui-space-3)
5453
- );
5667
+ .cell {
5668
+ width: 44px;
5669
+ height: 44px;
5454
5670
  }
5455
- :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
5456
- .field:where([data-hn-ui-input-group]) {
5457
- grid-row: 1;
5458
5671
  }
5459
- :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
5460
- .action:where([data-hn-ui-input-group]) {
5461
- grid-column: 2;
5462
- grid-row: 1;
5463
- align-self: start;
5464
- justify-self: end;
5465
- padding: var(--ui-space-2);
5672
+ @media (forced-colors: active) {
5673
+ .cell:focus-visible {
5674
+ outline-color: Highlight;
5466
5675
  }
5467
- .affix:empty:where([data-hn-ui-input-group]),.action:empty:where([data-hn-ui-input-group]) {
5468
- display: none;
5469
5676
  }
5470
- :scope[hidden]:where([data-hn-ui-input-group]) {
5677
+ :scope[hidden] {
5471
5678
  display: none;
5472
5679
  }
5473
5680
  }
@@ -9121,213 +9328,6 @@ input:where([data-hn-ui-switch]),input:where([data-hn-ui-switch])::after {
9121
9328
  }
9122
9329
  }
9123
9330
 
9124
- /* This component's hooks style the element they are set on, and never a nested one. */
9125
- @property --ui-textarea-border-width {
9126
- syntax: "*";
9127
- inherits: false;
9128
- }
9129
- @property --ui-textarea-border {
9130
- syntax: "*";
9131
- inherits: false;
9132
- }
9133
- @property --ui-textarea-border-hover {
9134
- syntax: "*";
9135
- inherits: false;
9136
- }
9137
- @property --ui-textarea-focus-border {
9138
- syntax: "*";
9139
- inherits: false;
9140
- }
9141
- @property --ui-textarea-focus-outline {
9142
- syntax: "*";
9143
- inherits: false;
9144
- }
9145
- @property --ui-textarea-surface {
9146
- syntax: "*";
9147
- inherits: false;
9148
- }
9149
- @property --ui-textarea-focus-shadow {
9150
- syntax: "*";
9151
- inherits: false;
9152
- }
9153
- @property --ui-textarea-invalid-border {
9154
- syntax: "*";
9155
- inherits: false;
9156
- }
9157
- @property --ui-textarea-invalid-focus-shadow {
9158
- syntax: "*";
9159
- inherits: false;
9160
- }
9161
- @property --ui-textarea-radius {
9162
- syntax: "*";
9163
- inherits: false;
9164
- }
9165
- @property --ui-textarea-shadow {
9166
- syntax: "*";
9167
- inherits: false;
9168
- }
9169
- @property --ui-textarea-padding-inline-end {
9170
- syntax: "*";
9171
- inherits: false;
9172
- }
9173
-
9174
- /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
9175
-
9176
- /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
9177
-
9178
- /* Below 16px, iOS zooms the page into the focused field, so touch keeps body-size text. */
9179
-
9180
- /* A component that sets its display still honours the hidden attribute on its root. */
9181
- @scope (.ui-textarea) to ([data-hn]) {
9182
- :scope {
9183
- box-sizing: border-box;
9184
- display: inline-block;
9185
- inline-size: 100%;
9186
- min-inline-size: 0;
9187
- min-block-size: calc(3rem * 2);
9188
- margin: 0;
9189
- --_padding-block: var(--ui-space-2);
9190
- padding: var(--ui-space-2) var(--ui-space-3);
9191
- padding-inline-end: var(
9192
- --ui-textarea-padding-inline-end,
9193
- var(--_ui-default-textarea-padding-inline-end, var(--ui-space-3))
9194
- );
9195
- appearance: none;
9196
- resize: vertical;
9197
- border: var(
9198
- --ui-textarea-border-width,
9199
- var(--_ui-default-textarea-border-width, var(--ui-border-width))
9200
- ) solid
9201
- var(
9202
- --ui-textarea-border,
9203
- var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong)))
9204
- );
9205
- border-radius: var(
9206
- --ui-textarea-radius,
9207
- var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)))
9208
- );
9209
- background: var(
9210
- --ui-textarea-surface,
9211
- var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated)))
9212
- );
9213
- color: var(--ui-text-primary);
9214
- font: inherit;
9215
- line-height: var(--ui-line-height-md);
9216
- box-shadow: var(
9217
- --ui-textarea-shadow,
9218
- var(--_ui-default-textarea-shadow, var(--ui-control-inset))
9219
- );
9220
- transition:
9221
- border-color var(--ui-motion-fast) var(--ui-motion-ease),
9222
- box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
9223
- background-color var(--ui-motion-fast) var(--ui-motion-ease);
9224
- }
9225
- :scope::selection {
9226
- background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
9227
- color: inherit;
9228
- }
9229
- :scope::placeholder {
9230
- color: var(--ui-control-placeholder, var(--ui-text-secondary));
9231
- opacity: 1;
9232
- }
9233
- :scope:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
9234
- border-color: var(
9235
- --ui-textarea-border-hover,
9236
- var(
9237
- --_ui-default-textarea-border-hover,
9238
- var(--ui-control-border-hover, var(--ui-text-secondary))
9239
- )
9240
- );
9241
- }
9242
- :scope:focus-visible {
9243
- border-color: var(
9244
- --ui-textarea-focus-border,
9245
- var(--_ui-default-textarea-focus-border, var(--ui-control-focus, var(--ui-accent-solid)))
9246
- );
9247
- outline: var(--ui-textarea-focus-outline, var(--_ui-default-textarea-focus-outline, none));
9248
- box-shadow: var(
9249
- --ui-textarea-focus-shadow,
9250
- var(
9251
- --_ui-default-textarea-focus-shadow,
9252
- var(--ui-control-inset),
9253
- var(--ui-control-focus-halo-shadow)
9254
- )
9255
- );
9256
- }
9257
- :scope[aria-invalid="true"],:scope:is(:user-invalid, [data-user-invalid]) {
9258
- border-color: var(
9259
- --ui-textarea-invalid-border,
9260
- var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))
9261
- );
9262
- }
9263
- :scope:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
9264
- box-shadow: var(
9265
- --ui-textarea-invalid-focus-shadow,
9266
- var(
9267
- --_ui-default-textarea-invalid-focus-shadow,
9268
- var(--ui-control-inset),
9269
- 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
9270
- in srgb,
9271
- var(
9272
- --ui-textarea-invalid-border,
9273
- var(
9274
- --_ui-default-textarea-invalid-border,
9275
- var(--ui-field-danger, var(--ui-danger-solid))
9276
- )
9277
- ) 22%,
9278
- transparent
9279
- )
9280
- )
9281
- );
9282
- }
9283
- :scope:disabled {
9284
- background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
9285
- color: var(--ui-disabled-text);
9286
- box-shadow: none;
9287
- cursor: not-allowed;
9288
- }
9289
- :scope[data-ui-textarea-state~="size=sm"] {
9290
- --_padding-block: var(--ui-space-1);
9291
- padding: var(--ui-space-1) var(--ui-space-2);
9292
- padding-inline-end: var(
9293
- --ui-textarea-padding-inline-end,
9294
- var(--_ui-default-textarea-padding-inline-end, var(--ui-space-2))
9295
- );
9296
- font-size: var(--ui-font-size-sm);
9297
- }
9298
- :scope[data-ui-textarea-state~="size=lg"] {
9299
- --_padding-block: var(--ui-space-3);
9300
- padding: var(--ui-space-3) var(--ui-space-4);
9301
- padding-inline-end: var(
9302
- --ui-textarea-padding-inline-end,
9303
- var(--_ui-default-textarea-padding-inline-end, var(--ui-space-4))
9304
- );
9305
- font-size: var(--ui-font-size-md);
9306
- }
9307
- :scope[data-ui-textarea-state~="maxRows"] {
9308
- max-block-size: calc(
9309
- max(var(--_rows), var(--_max-rows)) * 1lh + var(--_padding-block) + var(--_padding-block) +
9310
- 2px
9311
- );
9312
- }
9313
- @supports (field-sizing: content) {
9314
- :scope[data-ui-textarea-state~="autosize"] {
9315
- field-sizing: content;
9316
- min-block-size: calc(
9317
- var(--_rows) * 1lh + var(--_padding-block) + var(--_padding-block) + 2px
9318
- );
9319
- }
9320
- }
9321
- @media (pointer: coarse) {
9322
- :scope[data-ui-textarea-state~="size=sm"] {
9323
- font-size: inherit;
9324
- }
9325
- }
9326
- :scope[hidden] {
9327
- display: none;
9328
- }
9329
- }
9330
-
9331
9331
  /* Component hooks stay on this toast; group overlay values may inherit from the region. */
9332
9332
  @property --ui-toast-surface {
9333
9333
  syntax: "*";
@@ -10438,6 +10438,30 @@ html[data-ui-input-modality="touch"]
10438
10438
  :is([data-component~="ui-editor-surface"], .ui-editor-surface) .ProseMirror[data-link-navigation] a[href] {
10439
10439
  cursor: pointer;
10440
10440
  }
10441
+
10442
+ /* Inline table affordances share the document's scroll chain; popups use the top layer. */
10443
+ .looma-editor__table-overlay-shell {
10444
+ position: absolute;
10445
+ z-index: var(--ui-editor-overlay-z-index, 299);
10446
+ overflow: visible;
10447
+ pointer-events: none;
10448
+ }
10449
+
10450
+ /* The toolbar follows its table, lifting inside the document scroller only when needed. */
10451
+ [data-looma-table-toolbar-host] { display: contents; }
10452
+ .looma-editor__table-toolbar-shell {
10453
+ position: sticky;
10454
+ bottom: var(--ui-space-3);
10455
+ z-index: var(--ui-editor-popover-z-index, 300);
10456
+ width: max-content;
10457
+ max-width: 100%;
10458
+ margin: var(--ui-space-3) auto 0;
10459
+ overflow-x: auto;
10460
+ }
10461
+
10462
+ .looma-editor .ProseMirror [data-looma-table-block] + p {
10463
+ margin-top: var(--ui-editor-table-following-gap, var(--ui-space-4));
10464
+ }
10441
10465
  /**
10442
10466
  * Looma editor styles (tokens-based).
10443
10467
  * Tables, context menu, insert-table grid, slash menu. Use with ProseMirror/Tiptap content.
@@ -10738,7 +10762,6 @@ html[data-ui-input-modality="touch"]
10738
10762
 
10739
10763
  .looma-editor__table-picker-popover,
10740
10764
  .looma-editor__chip-popover,
10741
- .looma-editor__table-toolbar-shell,
10742
10765
  .looma-editor__table-menu-shell {
10743
10766
  z-index: var(--ui-editor-popover-z-index, 300);
10744
10767
  }
@@ -10768,9 +10791,7 @@ html[data-ui-input-modality="touch"]
10768
10791
  outline-offset: var(--ui-focus-width);
10769
10792
  }
10770
10793
 
10771
- .looma-editor__table-toolbar-shell,
10772
- .looma-editor__table-menu-shell,
10773
- .looma-editor__table-overlay-shell {
10794
+ .looma-editor__table-menu-shell {
10774
10795
  position: fixed;
10775
10796
  }
10776
10797
 
@@ -10836,12 +10857,6 @@ html[data-ui-input-modality="touch"]
10836
10857
  color: var(--ui-text-primary, #111827);
10837
10858
  }
10838
10859
 
10839
- .looma-editor__table-overlay-shell {
10840
- z-index: var(--ui-editor-overlay-z-index, 299);
10841
- overflow: visible;
10842
- pointer-events: none;
10843
- }
10844
-
10845
10860
  .looma-editor .ProseMirror .looma-mention {
10846
10861
  padding: 0.08em 0.28em;
10847
10862
  color: var(--ui-editor-mention-color, var(--ui-accent-active, var(--ui-accent-solid)));