ui-optago 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # UI Óptago
2
2
 
3
- Design System da **Óptago** — componentes UI em **CSS puro** + **JS vanilla**, sem Tailwind e sem build obrigatório. Tema escuro por padrão, tema claro automático, verde-lima `#C0FF6B` como cor de destaque e o octógono da marca como motivo visual.
3
+ [![npm](https://img.shields.io/npm/v/ui-optago.svg)](https://www.npmjs.com/package/ui-optago)
4
+ [![license](https://img.shields.io/npm/l/ui-optago.svg)](./LICENSE)
5
+
6
+ Design System da **[Óptago](https://optago.com.br)** — componentes UI em **CSS puro** + **JS vanilla**, sem Tailwind e sem build obrigatório. Tema escuro por padrão, tema claro automático, verde-lima `#C0FF6B` como cor de destaque e o octógono da marca como motivo visual.
4
7
 
5
8
  ## Instalação
6
9
 
@@ -15,13 +18,22 @@ npm install ui-optago
15
18
  <script src="node_modules/ui-optago/ui-optago.js"></script>
16
19
  ```
17
20
 
18
- **Via CDN (após publicado no npm):**
21
+ **Via CDN** (não precisa instalar nada — adicione ao `<head>`):
19
22
 
20
23
  ```html
21
24
  <link rel="stylesheet" href="https://unpkg.com/ui-optago/ui-optago.css">
22
25
  <script src="https://unpkg.com/ui-optago/ui-optago.js"></script>
23
26
  ```
24
27
 
28
+ Em produção, fixe a versão para não sofrer quebras em atualizações:
29
+
30
+ ```html
31
+ <link rel="stylesheet" href="https://unpkg.com/ui-optago@1.0.0/ui-optago.css">
32
+ <script src="https://unpkg.com/ui-optago@1.0.0/ui-optago.js"></script>
33
+ ```
34
+
35
+ > Também funciona via jsDelivr: `https://cdn.jsdelivr.net/npm/ui-optago/ui-optago.css`
36
+
25
37
  **Ou copie os arquivos direto** (`ui-optago.css` e `ui-optago.js`) para o seu projeto:
26
38
 
27
39
  ```html
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-optago",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Design System Óptago — componentes UI em CSS puro + JS vanilla, com tema claro/escuro, verde-lima (#C0FF6B) e app shell com sidebar colapsável.",
5
5
  "author": "Óptago <cowork@optago.com.br> (https://optago.com.br)",
6
6
  "license": "MIT",
package/ui-optago.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- UI ÓPTAGO — Design System v1.0
2
+ UI ÓPTAGO — Design System v1.0.2
3
3
  CSS puro, sem dependências. Tema escuro por padrão.
4
4
  Para tema claro: adicione a classe "light" ao <html> (o ui-optago.js
5
5
  gerencia isso automaticamente via localStorage).
@@ -74,12 +74,12 @@
74
74
 
75
75
  /* Superfícies — TEMA CLARO */
76
76
  html.light {
77
- --op-bg: #EEF2F7;
78
- --op-panel: #FFFFFF;
79
- --op-panel-2: #F6F8FB;
80
- --op-panel-3: #EDF1F6;
81
- --op-border: #DFE6EE;
82
- --op-border-2: #C6D2DF;
77
+ --op-bg: #E3E9F0;
78
+ --op-panel: #F8FAFC;
79
+ --op-panel-2: #EEF2F7;
80
+ --op-panel-3: #E6EBF2;
81
+ --op-border: #D6DEE7;
82
+ --op-border-2: #BDC9D8;
83
83
  --op-text: #334155;
84
84
  --op-strong: #0F172A;
85
85
  --op-muted: #64748B;
@@ -100,9 +100,9 @@ html.light {
100
100
  /* Fundo do tema claro: gradiente suave com um toque de verde-lima no topo */
101
101
  html.light body {
102
102
  background:
103
- radial-gradient(900px 420px at 75% -10%, rgba(192,255,107,.30) 0%, transparent 60%),
104
- radial-gradient(700px 380px at 10% 110%, rgba(192,255,107,.14) 0%, transparent 60%),
105
- linear-gradient(180deg, #F2F5F9 0%, #E9EEF5 100%);
103
+ radial-gradient(900px 420px at 75% -10%, rgba(192,255,107,.20) 0%, transparent 60%),
104
+ radial-gradient(700px 380px at 10% 110%, rgba(192,255,107,.10) 0%, transparent 60%),
105
+ linear-gradient(180deg, #E7ECF2 0%, #DEE5EC 100%);
106
106
  }
107
107
 
108
108
  /* ----------------------------------------------------------------------------
@@ -140,7 +140,9 @@ h4 { font-size: 1rem; }
140
140
 
141
141
  p { margin: 0 0 1em; }
142
142
  a { color: var(--op-accent); }
143
- html.light a { color: #3F6212; }
143
+ html.light a:not(.op-btn) {
144
+ color: #3F6212;
145
+ }
144
146
  code, kbd { font-family: var(--op-font-mono); font-size: .85em; }
145
147
 
146
148
  kbd {
@@ -206,6 +208,7 @@ html.light :focus-visible {
206
208
  height: 100vh;
207
209
  height: 100dvh;
208
210
  overflow: hidden;
211
+ overflow: clip;
209
212
  }
210
213
 
211
214
  .op-app-body {
@@ -214,6 +217,11 @@ html.light :focus-visible {
214
217
  display: flex;
215
218
  flex-direction: column;
216
219
  position: relative;
220
+ /* "clip" impede que .op-glow (ou qualquer filho absoluto) vaze para fora e
221
+ torne o documento rolável — sem isso, clicar em âncoras rola o viewport
222
+ e esconde o header. Nota: overflow no <body> é propagado ao viewport
223
+ pelo navegador, então o clip do .op-app sozinho não resolve. */
224
+ overflow: clip;
217
225
  }
218
226
 
219
227
  /* Header */
@@ -258,13 +266,8 @@ html.light .op-header { box-shadow: 0 8px 24px -14px rgba(15,23,42,.18); }
258
266
  display: flex; align-items: center; justify-content: center;
259
267
  flex-shrink: 0;
260
268
  }
261
- .op-brand-mark::after {
262
- content: "";
263
- width: 10px; height: 10px;
264
- background: #000;
265
- -webkit-clip-path: var(--op-oct); clip-path: var(--op-oct);
266
- }
267
- html.light .op-brand-mark { background: #000; }
269
+
270
+
268
271
  html.light .op-brand-mark::after { background: var(--op-accent); }
269
272
  .op-brand-name {
270
273
  font-family: var(--op-font-display);
@@ -278,12 +281,12 @@ html.light .op-brand-mark::after { background: var(--op-accent); }
278
281
  .op-main {
279
282
  flex: 1;
280
283
  overflow-y: auto;
281
- padding: 2rem 1.5rem;
284
+ padding: 1.5rem;
282
285
  scroll-behavior: smooth;
283
286
  position: relative;
284
287
  z-index: 1; /* mantém o conteúdo acima dos .op-glow do fundo */
285
288
  }
286
- @media (min-width: 1024px) { .op-main { padding: 2.5rem; } }
289
+ @media (min-width: 1024px) { .op-main { padding: 1.75rem 2rem; } }
287
290
 
288
291
  .op-container { max-width: 1100px; margin: 0 auto; }
289
292
 
@@ -357,25 +360,28 @@ html.light .op-brand-mark::after { background: var(--op-accent); }
357
360
  align-items: center;
358
361
  gap: .75rem;
359
362
  padding: .625rem .75rem;
363
+ border: 1px solid transparent;
360
364
  border-radius: var(--op-radius);
361
365
  color: var(--op-text);
362
366
  text-decoration: none;
363
367
  font-weight: 600;
364
368
  font-size: .875rem;
365
369
  white-space: nowrap;
366
- transition: background .2s ease, color .2s ease;
370
+ transition: background .2s ease, color .2s ease, border-color .2s ease;
367
371
  }
368
372
  .op-nav-item i { font-size: 1.25rem; flex-shrink: 0; }
369
373
  .op-nav-item:hover {
370
374
  background: color-mix(in srgb, var(--op-strong) 6%, transparent);
375
+ border-color: var(--op-border-2);
371
376
  color: var(--op-strong);
372
377
  }
373
378
  .op-nav-item.is-active {
374
- background: color-mix(in srgb, var(--op-accent) 12%, transparent);
379
+ background: color-mix(in srgb, var(--op-accent) 15%, transparent);
380
+ border-color: color-mix(in srgb, var(--op-accent) 45%, transparent);
375
381
  color: var(--op-strong);
376
382
  box-shadow: inset -3px 0 0 var(--op-accent);
377
383
  }
378
- html.light .op-nav-item.is-active { box-shadow: inset -3px 0 0 #84CC16; }
384
+ html.light .op-nav-item.is-active { box-shadow: inset -3px 0 0 #84CC16; border-color: color-mix(in srgb, #84CC16 45%, transparent); }
379
385
  .op-nav-item.is-active i { color: var(--op-accent); }
380
386
  html.light .op-nav-item.is-active i { color: #3F6212; }
381
387
 
@@ -384,7 +390,7 @@ html.light .op-nav-item.is-active i { color: #3F6212; }
384
390
  .op-sidebar.is-collapsed .op-nav-text,
385
391
  .op-sidebar.is-collapsed .op-sidebar-label,
386
392
  .op-sidebar.is-collapsed .op-sidebar-header-text { display: none; }
387
- .op-sidebar.is-collapsed .op-nav-item { justify-content: center; padding-left: 0; padding-right: 0; }
393
+ .op-sidebar.is-collapsed .op-nav-item { justify-content: center; padding: .625rem; }
388
394
 
389
395
  /* Tooltip do item (aparece à esquerda quando colapsada).
390
396
  position:fixed — o JS calcula top/right no hover, pois o overflow-y:auto
@@ -1488,17 +1494,53 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1488
1494
  .op-toast-danger .op-toast-icon { background: color-mix(in srgb, var(--op-danger) 15%, transparent); color: var(--op-danger); border-color: color-mix(in srgb, var(--op-danger) 30%, transparent); }
1489
1495
 
1490
1496
  /* ----------------------------------------------------------------------------
1491
- 20. BLOCO DE DEMONSTRAÇÃO (showcase: preview + copiar + ver código)
1497
+ 20. TELA DE CARREGAMENTO (overlay em tela cheia)
1498
+ Uso: OptagoUI.showLoading('Mensagem...') / OptagoUI.hideLoading()
1499
+ ou data-op-loading-form no <form> para acionar sozinho no submit.
1500
+ ---------------------------------------------------------------------------- */
1501
+ .op-loading-overlay {
1502
+ position: fixed;
1503
+ inset: 0;
1504
+ z-index: 150;
1505
+ display: none;
1506
+ flex-direction: column;
1507
+ align-items: center;
1508
+ justify-content: center;
1509
+ gap: 1.25rem;
1510
+ background: color-mix(in srgb, var(--op-bg) 85%, transparent);
1511
+ backdrop-filter: blur(8px);
1512
+ -webkit-backdrop-filter: blur(8px);
1513
+ opacity: 0;
1514
+ transition: opacity .25s var(--op-ease);
1515
+ }
1516
+ .op-loading-overlay.is-open { display: flex; }
1517
+ .op-loading-overlay.is-visible { opacity: 1; }
1518
+ .op-loading-overlay .op-spinner { width: 46px; height: 46px; border-width: 3px; }
1519
+ .op-loading-message {
1520
+ max-width: 320px;
1521
+ padding: 0 1.5rem;
1522
+ font-family: var(--op-font-mono);
1523
+ font-size: .875rem;
1524
+ font-weight: 600;
1525
+ letter-spacing: .01em;
1526
+ text-align: center;
1527
+ color: var(--op-strong);
1528
+ }
1529
+
1530
+ /* ----------------------------------------------------------------------------
1531
+ 21. BLOCO DE DEMONSTRAÇÃO (showcase: preview + copiar + ver código)
1492
1532
  ---------------------------------------------------------------------------- */
1493
1533
  .op-demo {
1494
1534
  background: var(--op-panel);
1495
1535
  border: 1px solid var(--op-border);
1496
1536
  border-radius: var(--op-radius-lg);
1497
- overflow: hidden;
1498
1537
  box-shadow: var(--op-shadow);
1499
1538
  }
1539
+ /* Sem overflow:hidden no container: dropdowns/tooltips exibidos dentro do
1540
+ preview não podem ser cortados. O arredondamento é feito nos filhos. */
1500
1541
  .op-demo-preview {
1501
- padding: 2rem;
1542
+ padding: 1.5rem;
1543
+ border-radius: var(--op-radius-lg) var(--op-radius-lg) 0 0;
1502
1544
  background:
1503
1545
  radial-gradient(color-mix(in srgb, var(--op-border) 55%, transparent) 1px, transparent 1px);
1504
1546
  background-size: 22px 22px;
@@ -1510,7 +1552,11 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1510
1552
  padding: .625rem .875rem;
1511
1553
  border-top: 1px solid var(--op-border);
1512
1554
  background: var(--op-panel-2);
1555
+ border-radius: 0 0 var(--op-radius-lg) var(--op-radius-lg);
1513
1556
  }
1557
+ /* Quando o código-fonte está visível, ele passa a ser o último bloco:
1558
+ a barra perde o arredondamento inferior e o código assume. */
1559
+ .op-demo-bar:has(+ .op-demo-code.is-visible) { border-radius: 0; }
1514
1560
  .op-demo-code {
1515
1561
  display: none;
1516
1562
  margin: 0;
@@ -1524,6 +1570,7 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1524
1570
  overflow-x: auto;
1525
1571
  tab-size: 2;
1526
1572
  white-space: pre;
1573
+ border-radius: 0 0 var(--op-radius-lg) var(--op-radius-lg);
1527
1574
  }
1528
1575
  .op-demo-code.is-visible { display: block; }
1529
1576
 
@@ -1532,14 +1579,14 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1532
1579
  display: flex;
1533
1580
  align-items: center;
1534
1581
  gap: .75rem;
1535
- padding-bottom: 1rem;
1536
- margin-bottom: 1.5rem;
1582
+ padding-bottom: .75rem;
1583
+ margin-bottom: 1.125rem;
1537
1584
  border-bottom: 1px solid var(--op-border);
1538
1585
  }
1539
1586
  .op-section-head i {
1540
- font-size: 1.375rem;
1587
+ font-size: 1.25rem;
1541
1588
  color: var(--op-accent);
1542
- width: 44px; height: 44px;
1589
+ width: 38px; height: 38px;
1543
1590
  display: flex;
1544
1591
  align-items: center;
1545
1592
  justify-content: center;
@@ -1547,7 +1594,7 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1547
1594
  -webkit-clip-path: var(--op-oct); clip-path: var(--op-oct);
1548
1595
  flex-shrink: 0;
1549
1596
  }
1550
- html.light .op-section-head i { color: #3F6212; background: #EDFBD4; }
1597
+ html.light .op-section-head i { color: #3F6212; background: var(--op-accent); }
1551
1598
  .op-section-head h2 { margin: 0; font-size: 1.5rem; }
1552
1599
 
1553
1600
  /* ----------------------------------------------------------------------------
@@ -1567,13 +1614,21 @@ html.light .op-section-head i { color: #3F6212; background: #EDFBD4; }
1567
1614
  pointer-events: none;
1568
1615
  z-index: 0;
1569
1616
  }
1570
- html.light .op-glow { opacity: .22; }
1571
1617
  .op-glow-info { background: var(--op-info); }
1572
- html.light .op-glow-info { opacity: .14; }
1573
1618
  .op-glow-sm { width: 260px; height: 200px; }
1574
1619
  .op-glow-lg { width: 700px; height: 460px; }
1575
1620
  .op-glow-strong { opacity: .12; }
1576
- html.light .op-glow-strong { opacity: .32; }
1621
+
1622
+ /* No tema claro o verde-lima #C0FF6B é claro demais para aparecer sobre fundo
1623
+ branco com opacidade baixa. Usamos um verde mais saturado + mix-blend-mode:
1624
+ multiply (tinge o fundo em vez de clarear) para o glow ficar visível. */
1625
+ html.light .op-glow {
1626
+ background: #9AE600;
1627
+ opacity: .40;
1628
+ mix-blend-mode: multiply;
1629
+ }
1630
+ html.light .op-glow-info { background: #3B82F6; opacity: .28; }
1631
+ html.light .op-glow-strong { opacity: .55; }
1577
1632
 
1578
1633
  /* ----------------------------------------------------------------------------
1579
1634
  20c. MODAIS — VARIANTES EXTRAS
@@ -1858,7 +1913,7 @@ html.light .op-tabulator.tabulator .tabulator-footer .tabulator-page.active {
1858
1913
  .op-tabulator .op-progress { min-width: 90px; }
1859
1914
 
1860
1915
  /* ----------------------------------------------------------------------------
1861
- 21. UTILITÁRIOS
1916
+ 22. UTILITÁRIOS
1862
1917
  ---------------------------------------------------------------------------- */
1863
1918
  .op-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
1864
1919
  .op-stack { display: flex; flex-direction: column; gap: 1rem; }
@@ -1877,11 +1932,11 @@ html.light .op-text-accent { color: #3F6212; }
1877
1932
  .op-text-sm { font-size: .8125rem; }
1878
1933
  .op-text-xs { font-size: .6875rem; }
1879
1934
 
1880
- .op-mt-1 { margin-top: .5rem; } .op-mt-2 { margin-top: 1rem; } .op-mt-3 { margin-top: 1.5rem; } .op-mt-4 { margin-top: 2.5rem; }
1881
- .op-mb-1 { margin-bottom: .5rem; } .op-mb-2 { margin-bottom: 1rem; } .op-mb-3 { margin-bottom: 1.5rem; } .op-mb-4 { margin-bottom: 2.5rem; }
1935
+ .op-mt-1 { margin-top: .5rem; } .op-mt-2 { margin-top: 1rem; } .op-mt-3 { margin-top: 1.5rem; } .op-mt-4 { margin-top: 1.75rem; }
1936
+ .op-mb-1 { margin-bottom: .5rem; } .op-mb-2 { margin-bottom: 1rem; } .op-mb-3 { margin-bottom: 1.5rem; } .op-mb-4 { margin-bottom: 1.75rem; }
1882
1937
 
1883
1938
  /* ----------------------------------------------------------------------------
1884
- 22. ANIMAÇÕES
1939
+ 23. ANIMAÇÕES
1885
1940
  ---------------------------------------------------------------------------- */
1886
1941
  @keyframes op-spin { to { transform: rotate(360deg); } }
1887
1942
  @keyframes op-bounce {
package/ui-optago.html CHANGED
@@ -3,11 +3,31 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>UI Óptago — Design System</title>
7
6
 
8
- <!-- UI Óptago: o JS injeta fontes e ícones automaticamente e aplica o tema salvo -->
9
- <link rel="stylesheet" href="ui-optago.css">
10
- <script src="ui-optago.js"></script>
7
+ <!-- SEO -->
8
+ <title>UI Óptago | Design System em CSS puro & JS vanilla</title>
9
+ <meta name="description" content="Design System da Óptago: biblioteca de componentes UI em CSS puro e JavaScript vanilla, com tema claro/escuro, verde-lima #C0FF6B e app shell com sidebar colapsável. Sem Tailwind, sem build.">
10
+ <meta name="keywords" content="design system, ui, css puro, componentes, vanilla js, dark mode, óptago">
11
+ <meta name="author" content="Óptago">
12
+ <meta name="theme-color" content="#050505" media="(prefers-color-scheme: dark)">
13
+ <meta name="theme-color" content="#EEF2F7" media="(prefers-color-scheme: light)">
14
+ <link rel="canonical" href="https://optago.com.br/">
15
+
16
+ <!-- Open Graph / redes sociais -->
17
+ <meta property="og:type" content="website">
18
+ <meta property="og:title" content="UI Óptago Design System">
19
+ <meta property="og:description" content="Componentes UI em CSS puro e JS vanilla, com tema claro/escuro e o verde-lima da marca Óptago.">
20
+ <meta property="og:locale" content="pt_BR">
21
+ <meta property="og:site_name" content="Óptago">
22
+ <meta name="twitter:card" content="summary_large_image">
23
+ <meta name="twitter:title" content="UI Óptago Design System">
24
+ <meta name="twitter:description" content="Componentes UI em CSS puro e JS vanilla, com tema claro/escuro e o verde-lima da marca Óptago.">
25
+
26
+ <!-- Favicon: octógono verde-lima da marca, embutido em SVG (sem arquivo externo) -->
27
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><polygon points=%2230,4 70,4 96,30 96,70 70,96 30,96 4,70 4,30%22 fill=%22%23C0FF6B%22/></svg>">
28
+
29
+ <link rel="stylesheet" href="./ui-optago.css">
30
+ <script src="./ui-optago.js"></script>
11
31
  </head>
12
32
 
13
33
  <body class="op-app">
@@ -15,7 +35,7 @@
15
35
  <!-- ==================== COLUNA PRINCIPAL (header + main + footer) ==================== -->
16
36
  <div class="op-app-body">
17
37
 
18
- <!-- Glows de fundo (verde-lima desfocado)ficam atrás do conteúdo -->
38
+ <!-- Glows de fundo (verde-lima desfocado) ficam atrás do conteúdo -->
19
39
  <div class="op-glow op-glow-lg" style="top:-140px; left:22%;"></div>
20
40
  <div class="op-glow op-glow-sm" style="bottom:10%; left:-80px;"></div>
21
41
  <div class="op-glow op-glow-info op-glow-sm" style="bottom:-60px; right:8%;"></div>
@@ -29,8 +49,8 @@
29
49
  </div>
30
50
  <div class="op-header-divider"></div>
31
51
  <div class="op-header-group">
32
- <strong style="font-family:var(--op-font-display); font-size:.875rem;">UI Components</strong>
33
- <span class="op-badge op-badge-brand op-badge-pill"><span class="op-dot op-dot-pulse"></span>v1.0</span>
52
+ <strong style="font-family:var(--op-font-display); font-size:.875rem;">UI</strong>
53
+ <span class="op-badge op-badge-brand op-badge-pill"><span class="op-dot op-dot-pulse"></span>v1.0.2</span>
34
54
  </div>
35
55
  </div>
36
56
  <div class="op-header-group">
@@ -38,6 +58,7 @@
38
58
  <i class="ph ph-moon op-icon-moon"></i>
39
59
  <i class="ph ph-sun op-icon-sun" style="display:none"></i>
40
60
  </button>
61
+
41
62
  <!-- Hamburguer: aparece só no mobile para abrir o drawer -->
42
63
  <button class="op-btn op-btn-icon op-mobile-only" data-op-sidebar-mobile>
43
64
  <i class="ph ph-list"></i>
@@ -52,11 +73,21 @@
52
73
  <section class="op-mb-4">
53
74
  <h1>Design System Óptago</h1>
54
75
  <p class="op-text-muted" style="max-width:640px;">
55
- Biblioteca completa de componentes em <strong>CSS puro</strong>sem Tailwind, sem build.
76
+ Biblioteca completa de componentes em <strong>CSS puro</strong> sem Tailwind, sem build.
56
77
  Inclua <code>ui-optago.css</code> e <code>ui-optago.js</code> no seu projeto e use as classes
57
78
  com prefixo <code>op-</code>. Em cada bloco abaixo, use <strong>Copiar</strong> para levar o
58
79
  HTML direto pro seu projeto, ou <strong>Ver código</strong> para inspecionar.
59
80
  </p>
81
+
82
+ <div class="op-alert op-alert-info op-mt-2 ">
83
+ <i class="ph ph-cloud-arrow-down"></i>
84
+ <div style="min-width:0; flex:1;">
85
+ <p class="op-alert-title">Usar via CDN</p>
86
+ <p class="op-alert-text op-mb-1">Sem baixar nada adicione as duas linhas ao <code>&lt;head&gt;</code>:</p>
87
+ <pre class="op-demo-code is-visible" style="border:1px solid var(--op-border); border-radius:var(--op-radius-sm); margin:0;">&lt;link rel="stylesheet" href="https://unpkg.com/ui-optago/ui-optago.css"&gt;
88
+ &lt;script src="https://unpkg.com/ui-optago/ui-optago.js"&gt;&lt;/script&gt;</pre>
89
+ </div>
90
+ </div>
60
91
  </section>
61
92
 
62
93
  <!-- ==================== 1. CORES ==================== -->
@@ -68,19 +99,19 @@
68
99
  <div class="op-grid-4 op-mb-4">
69
100
  <div class="op-card op-card-hover" style="padding:0; overflow:hidden;">
70
101
  <div style="height:72px; background:#000;"></div>
71
- <div style="padding:.875rem 1rem;"><strong>PretoBase</strong><br><code class="op-text-muted op-text-xs">#000000</code></div>
102
+ <div style="padding:.875rem 1rem;"><strong>Preto Base</strong><br><code class="op-text-muted op-text-xs">#000000</code></div>
72
103
  </div>
73
104
  <div class="op-card op-card-hover" style="padding:0; overflow:hidden;">
74
105
  <div style="height:72px; background:#C0FF6B;"></div>
75
- <div style="padding:.875rem 1rem;"><strong>Verde-limaDestaque</strong><br><code class="op-text-muted op-text-xs">#C0FF6B · --op-accent</code></div>
106
+ <div style="padding:.875rem 1rem;"><strong>Verde-lima Destaque</strong><br><code class="op-text-muted op-text-xs">#C0FF6B · --op-accent</code></div>
76
107
  </div>
77
108
  <div class="op-card op-card-hover" style="padding:0; overflow:hidden;">
78
109
  <div style="height:72px; background:#D5D5D5;"></div>
79
- <div style="padding:.875rem 1rem;"><strong>Cinza-claroTexto</strong><br><code class="op-text-muted op-text-xs">#D5D5D5 · --op-text</code></div>
110
+ <div style="padding:.875rem 1rem;"><strong>Cinza-claro Texto</strong><br><code class="op-text-muted op-text-xs">#D5D5D5 · --op-text</code></div>
80
111
  </div>
81
112
  <div class="op-card op-card-hover" style="padding:0; overflow:hidden;">
82
113
  <div style="height:72px; background:#656565;"></div>
83
- <div style="padding:.875rem 1rem;"><strong>Cinza-chumboApoio</strong><br><code class="op-text-muted op-text-xs">#656565 · --op-muted</code></div>
114
+ <div style="padding:.875rem 1rem;"><strong>Cinza-chumbo Apoio</strong><br><code class="op-text-muted op-text-xs">#656565 · --op-muted</code></div>
84
115
  </div>
85
116
  </div>
86
117
 
@@ -116,14 +147,14 @@
116
147
  </div>
117
148
  <div class="op-demo">
118
149
  <div class="op-demo-preview" id="d-tipografia">
119
- <h1>Heading 1Chakra Petch</h1>
120
- <h2>Heading 2Seções</h2>
121
- <h3>Heading 3Cards e blocos</h3>
122
- <h4>Heading 4Subtítulos</h4>
150
+ <h1>Heading 1 Chakra Petch</h1>
151
+ <h2>Heading 2 Seções</h2>
152
+ <h3>Heading 3 Cards e blocos</h3>
153
+ <h4>Heading 4 Subtítulos</h4>
123
154
  <p>Parágrafo padrão em <strong>Inter</strong>. Criamos e mantemos produtos próprios e sob medida,
124
155
  sempre com a mesma régua: proteger dados, reduzir atrito e manter tudo no ar.</p>
125
156
  <p class="op-text-muted">Texto secundário / muted para descrições e apoio.</p>
126
- <p class="op-text-mono op-text-sm">JetBrains Monodados técnicos, códigos e rótulos. <span class="op-text-accent">optago.com.br · 0 cookies</span></p>
157
+ <p class="op-text-mono op-text-sm">JetBrains Mono dados técnicos, códigos e rótulos. <span class="op-text-accent">optago.com.br</span></p>
127
158
  <p>Atalho de teclado: <kbd>Ctrl</kbd> + <kbd>K</kbd> · código inline: <code>--op-accent: #C0FF6B</code></p>
128
159
  </div>
129
160
  <div class="op-demo-bar">
@@ -318,7 +349,7 @@
318
349
  <label class="op-switch"><input type="checkbox"> <span>Modo manutenção</span></label>
319
350
  </div>
320
351
  <div class="op-field" style="max-width:320px;">
321
- <label class="op-label" for="f-range">Range75%</label>
352
+ <label class="op-label" for="f-range">Range 75%</label>
322
353
  <input class="op-range" id="f-range" type="range" min="0" max="100" value="75">
323
354
  </div>
324
355
  </div>
@@ -412,12 +443,12 @@
412
443
  </div>
413
444
  </div>
414
445
 
415
- <h3 class="op-mt-3 op-mb-2"><i class="ph ph-lightning op-text-accent"></i> Tabela avançadaTabulator</h3>
446
+ <h3 class="op-mt-3 op-mb-2"><i class="ph ph-lightning op-text-accent"></i> Tabela avançada Tabulator</h3>
416
447
  <p class="op-text-muted op-text-sm op-mb-2">
417
448
  Para tabelas com ordenação, paginação e filtros, use a variante com
418
449
  <a href="https://tabulator.info" target="_blank" rel="noopener">Tabulator</a> e tema Óptago.
419
450
  A biblioteca é carregada sob demanda: <code>OptagoUI.tabulator('#minhaTabela', opções)</code>
420
- clique nos cabeçalhos para ordenar.
451
+ clique nos cabeçalhos para ordenar.
421
452
  </p>
422
453
  <div class="op-demo">
423
454
  <div class="op-demo-preview" id="d-tabulator-wrap">
@@ -638,7 +669,7 @@ OptagoUI.tabulator('#minhaTabela', {
638
669
  <p class="op-text-muted" style="margin:0;">Ajuste preferências, chaves de API e integrações.</p>
639
670
  </div>
640
671
  <div class="op-tab-panel" data-op-tab-panel="logs">
641
- <p class="op-text-mono op-text-sm" style="margin:0;">[12:04:31] INFOserviço reiniciado com sucesso</p>
672
+ <p class="op-text-mono op-text-sm" style="margin:0;">[12:04:31] INFO serviço reiniciado com sucesso</p>
642
673
  </div>
643
674
  </div>
644
675
  <div data-op-tab-group="exemplo2" class="op-mb-3">
@@ -704,11 +735,11 @@ OptagoUI.tabulator('#minhaTabela', {
704
735
  <div class="op-accordion">
705
736
  <details open>
706
737
  <summary>O que é a Óptago?</summary>
707
- <div class="op-accordion-body">Criamos e mantemos produtos próprios (Comanda Rápida, Stats Rails, Brasil Buscador) e prestamos serviços sob medidasempre com a mesma régua: segurança, privacidade e performance.</div>
738
+ <div class="op-accordion-body">Criamos e mantemos produtos próprios (Comanda Rápida, Stats Rails, Brasil Buscador) e prestamos serviços sob medida sempre com a mesma régua: segurança, privacidade e performance.</div>
708
739
  </details>
709
740
  <details>
710
741
  <summary>O site usa cookies?</summary>
711
- <div class="op-accordion-body">Não. Coleta mínima de dados: o site institucional não usa cookies nem rastreadores.</div>
742
+ <div class="op-accordion-body">Usamos apenas 1 cookie, para lembrar se você prefere o tema claro ou escuro. Nenhum rastreador ou coleta de dados além disso.</div>
712
743
  </details>
713
744
  <details>
714
745
  <summary>Como funciona o suporte?</summary>
@@ -829,7 +860,7 @@ OptagoUI.tabulator('#minhaTabela', {
829
860
  <span class="op-timeline-marker is-warning"></span>
830
861
  <span class="op-timeline-time">Hoje · 11:20</span>
831
862
  <p class="op-timeline-title">Alerta de uso de disco</p>
832
- <p class="op-timeline-text">Volume principal atingiu 85%limpeza automática agendada.</p>
863
+ <p class="op-timeline-text">Volume principal atingiu 85% limpeza automática agendada.</p>
833
864
  </li>
834
865
  <li class="op-timeline-item">
835
866
  <span class="op-timeline-marker is-danger"></span>
@@ -856,22 +887,22 @@ OptagoUI.tabulator('#minhaTabela', {
856
887
  <section id="efeitos" class="op-mb-4" style="scroll-margin-top:1rem;">
857
888
  <div class="op-section-head">
858
889
  <i class="ph ph-sparkle"></i>
859
- <h2>EfeitosGlow verde-lima</h2>
890
+ <h2>Efeitos Glow verde-lima</h2>
860
891
  </div>
861
892
  <p class="op-text-muted op-text-sm op-mb-2">
862
- Manchas desfocadas de verde-lima para dar profundidade ao fundoesta própria página usa três
893
+ Manchas desfocadas de verde-lima para dar profundidade ao fundo esta própria página usa três
863
894
  (topo e cantos). Posicione <code>.op-glow</code> dentro de um container com
864
895
  <code>position:relative</code>; o conteúdo por cima deve ter <code>z-index</code> maior.
865
896
  Variações: <code>op-glow-sm</code>, <code>op-glow-lg</code>, <code>op-glow-strong</code>, <code>op-glow-info</code>.
866
897
  </p>
867
898
  <div class="op-demo">
868
899
  <div class="op-demo-preview" style="padding:0;">
869
- <div id="d-efeitos" style="position:relative; overflow:hidden; padding:3.5rem 2rem; border-radius:0;">
900
+ <div id="d-efeitos" style="position:relative; overflow:clip; padding:3.5rem 2rem; border-radius:0;">
870
901
  <div class="op-glow op-glow-strong" style="top:-120px; left:-60px;"></div>
871
902
  <div class="op-glow op-glow-sm op-glow-strong" style="bottom:-100px; right:10%;"></div>
872
903
  <div style="position:relative; z-index:1; text-align:center; max-width:520px; margin:0 auto;">
873
904
  <h2 style="margin-bottom:.5rem;">Hero com glow de fundo</h2>
874
- <p class="op-text-muted op-mb-2">Segurança, privacidade e performancea mesma régua em todos os produtos.</p>
905
+ <p class="op-text-muted op-mb-2">Segurança, privacidade e performance a mesma régua em todos os produtos.</p>
875
906
  <button class="op-btn op-btn-primary"><i class="ph ph-rocket-launch"></i> Começar agora</button>
876
907
  </div>
877
908
  </div>
@@ -906,7 +937,34 @@ OptagoUI.tabulator('#minhaTabela', {
906
937
  </div>
907
938
  </section>
908
939
 
909
- <!-- ==================== 17. MODAIS ==================== -->
940
+ <!-- ==================== 17. CARREGAMENTO ==================== -->
941
+ <section id="carregamento" class="op-mb-4" style="scroll-margin-top:1rem;">
942
+ <div class="op-section-head">
943
+ <i class="ph ph-circle-notch"></i>
944
+ <h2>Tela de carregamento</h2>
945
+ </div>
946
+ <div class="op-demo">
947
+ <div class="op-demo-preview" id="d-carregamento">
948
+ <p class="op-text-muted op-text-sm" style="margin-top:0;">Overlay em tela cheia para usar ao submeter formulários ou aguardar uma ação. API: <code>OptagoUI.showLoading(mensagem)</code> / <code>OptagoUI.hideLoading()</code>.</p>
949
+ <div class="op-row">
950
+ <button class="op-btn op-btn-primary" onclick="OptagoUI.showLoading('Salvando alterações...'); setTimeout(OptagoUI.hideLoading, 2200)">
951
+ <i class="ph ph-floppy-disk"></i> Disparar via JS
952
+ </button>
953
+ <form data-op-loading-form data-op-loading-message="Enviando formulário..." onsubmit="event.preventDefault(); setTimeout(OptagoUI.hideLoading, 2200)">
954
+ <button class="op-btn op-btn-secondary" type="submit">
955
+ <i class="ph ph-paper-plane-tilt"></i> Disparar via submit do form
956
+ </button>
957
+ </form>
958
+ </div>
959
+ </div>
960
+ <div class="op-demo-bar">
961
+ <button class="op-btn op-btn-ghost op-btn-sm" data-op-code="#d-carregamento"><i class="ph ph-code"></i> Ver código</button>
962
+ <button class="op-btn op-btn-secondary op-btn-sm" data-op-copy="#d-carregamento"><i class="ph ph-copy"></i> Copiar</button>
963
+ </div>
964
+ </div>
965
+ </section>
966
+
967
+ <!-- ==================== 18. MODAIS ==================== -->
910
968
  <section id="modais" class="op-mb-4" style="scroll-margin-top:1rem;">
911
969
  <div class="op-section-head">
912
970
  <i class="ph ph-app-window"></i>
@@ -939,17 +997,17 @@ OptagoUI.tabulator('#minhaTabela', {
939
997
  </div>
940
998
  </section>
941
999
 
942
- <!-- ==================== 18. LAYOUT ==================== -->
1000
+ <!-- ==================== 19. LAYOUT ==================== -->
943
1001
  <section id="layout" class="op-mb-4" style="scroll-margin-top:1rem;">
944
1002
  <div class="op-section-head">
945
1003
  <i class="ph ph-layout"></i>
946
- <h2>LayoutApp Shell</h2>
1004
+ <h2>Layout App Shell</h2>
947
1005
  </div>
948
1006
  <div class="op-alert op-alert-info op-mb-2">
949
1007
  <i class="ph ph-info"></i>
950
1008
  <div>
951
1009
  <p class="op-alert-title">Esta própria página usa o app shell</p>
952
- <p class="op-alert-text">Header, footer e sidebar (direita) são fixosapenas o conteúdo central rola. A sidebar colapsa no desktop (só ícones + tooltip) e vira drawer no mobile.</p>
1010
+ <p class="op-alert-text">Header, footer e sidebar (direita) são fixos apenas o conteúdo central rola. A sidebar colapsa no desktop (só ícones + tooltip) e vira drawer no mobile.</p>
953
1011
  </div>
954
1012
  </div>
955
1013
  <div class="op-demo">
@@ -979,8 +1037,8 @@ OptagoUI.tabulator('#minhaTabela', {
979
1037
 
980
1038
  <!-- FOOTER (fixo) -->
981
1039
  <footer class="op-footer">
982
- <span>ÓPTAGO © 2026Design System v1.0</span>
983
- <span>optago.com.br · 0 cookies</span>
1040
+ <span>ÓPTAGO © 2026 Design System v1.0.2</span>
1041
+ <span><a href="https://optago.com.br" target="_blank" rel="noopener">optago.com.br</a> · 1 cookie (só p/ tema)</span>
984
1042
  </footer>
985
1043
  </div>
986
1044
 
@@ -1014,6 +1072,7 @@ OptagoUI.tabulator('#minhaTabela', {
1014
1072
  <a class="op-nav-item" href="#efeitos"><i class="ph ph-sparkle"></i><span class="op-nav-text">Efeitos / Glow</span><span class="op-nav-tooltip">Efeitos / Glow</span></a>
1015
1073
  <div class="op-sidebar-label">Interativos</div>
1016
1074
  <a class="op-nav-item" href="#toasts"><i class="ph ph-bell-ringing"></i><span class="op-nav-text">Toasts</span><span class="op-nav-tooltip">Toasts</span></a>
1075
+ <a class="op-nav-item" href="#carregamento"><i class="ph ph-circle-notch"></i><span class="op-nav-text">Carregamento</span><span class="op-nav-tooltip">Carregamento</span></a>
1017
1076
  <a class="op-nav-item" href="#modais"><i class="ph ph-app-window"></i><span class="op-nav-text">Modais</span><span class="op-nav-tooltip">Modais</span></a>
1018
1077
  <a class="op-nav-item" href="#layout"><i class="ph ph-layout"></i><span class="op-nav-text">Layout</span><span class="op-nav-tooltip">Layout</span></a>
1019
1078
  </nav>
package/ui-optago.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- UI ÓPTAGO — Design System v1.0 (JavaScript)
2
+ UI ÓPTAGO — Design System v1.0.2 (JavaScript)
3
3
  Sem dependências de build. Carregue no <head> SEM defer para evitar
4
4
  flash de tema errado (FOUC):
5
5
 
@@ -21,9 +21,13 @@
21
21
  data-op-dismiss → remove o elemento pai .op-alert
22
22
  data-op-copy="#seletor" → copia o HTML do elemento alvo
23
23
  data-op-code="#seletor" → mostra/esconde o código-fonte do alvo
24
+ data-op-loading-form → no <form>: mostra a tela de carregamento
25
+ sozinho ao submeter (mensagem opcional em
26
+ data-op-loading-message="Enviando...")
24
27
 
25
28
  API global: OptagoUI.toast(msg, tipo, ms) · OptagoUI.openModal(sel)
26
29
  OptagoUI.closeModal(sel) · OptagoUI.setTheme('dark'|'light')
30
+ OptagoUI.showLoading(msg) · OptagoUI.hideLoading()
27
31
  ============================================================================ */
28
32
  (function () {
29
33
  'use strict';
@@ -241,6 +245,45 @@
241
245
  });
242
246
  };
243
247
 
248
+ /* --------------------------------------------------------------------------
249
+ 5c. TELA DE CARREGAMENTO — overlay em tela cheia
250
+ Uso: OptagoUI.showLoading('Salvando...') ... OptagoUI.hideLoading()
251
+ ou data-op-loading-form no <form> para acionar sozinho no submit.
252
+ -------------------------------------------------------------------------- */
253
+ var LOADING_ID = 'opLoadingOverlay';
254
+
255
+ function getLoadingOverlay() {
256
+ var ov = document.getElementById(LOADING_ID);
257
+ if (!ov) {
258
+ ov = document.createElement('div');
259
+ ov.id = LOADING_ID;
260
+ ov.className = 'op-loading-overlay';
261
+ ov.setAttribute('role', 'alert');
262
+ ov.setAttribute('aria-live', 'assertive');
263
+ ov.innerHTML =
264
+ '<span class="op-spinner"></span>' +
265
+ '<span class="op-loading-message"></span>';
266
+ document.body.appendChild(ov);
267
+ }
268
+ return ov;
269
+ }
270
+
271
+ OptagoUI.showLoading = function (message) {
272
+ var ov = getLoadingOverlay();
273
+ ov.querySelector('.op-loading-message').textContent = message || 'Carregando...';
274
+ ov.classList.add('is-open');
275
+ requestAnimationFrame(function () {
276
+ requestAnimationFrame(function () { ov.classList.add('is-visible'); });
277
+ });
278
+ };
279
+
280
+ OptagoUI.hideLoading = function () {
281
+ var ov = document.getElementById(LOADING_ID);
282
+ if (!ov) return;
283
+ ov.classList.remove('is-visible');
284
+ setTimeout(function () { ov.classList.remove('is-open'); }, 250);
285
+ };
286
+
244
287
  /* --------------------------------------------------------------------------
245
288
  6. HELPERS DO SHOWCASE — copiar HTML / ver código
246
289
  -------------------------------------------------------------------------- */
@@ -395,6 +438,14 @@
395
438
  tip.style.left = 'auto';
396
439
  });
397
440
 
441
+ // Formulário com data-op-loading-form: mostra a tela de carregamento
442
+ // sozinho ao submeter (mensagem em data-op-loading-message, se houver).
443
+ document.addEventListener('submit', function (e) {
444
+ var form = e.target.closest('[data-op-loading-form]');
445
+ if (!form) return;
446
+ OptagoUI.showLoading(form.getAttribute('data-op-loading-message'));
447
+ });
448
+
398
449
  // ESC fecha modais e dropdowns
399
450
  document.addEventListener('keydown', function (e) {
400
451
  if (e.key !== 'Escape') return;