Chandra

 view release on metacpan or  search on metacpan

Chandra.xs  view on Meta::CPAN

"el.style.cssText='pointer-events:auto;display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:var(--chandra-radius,6px);background:var(--chandra-surface,#1e1e1e);color:var(--chandra-text,#e0e0e0);box-shadow:var(--chandra-shadow,0 2p...
"var ic=document.createElement('span');"
"ic.style.cssText='font-size:1.2em;flex-shrink:0;';"
"ic.textContent=o.icon;el.appendChild(ic);"
"var bd=document.createElement('div');"
"bd.style.cssText='flex:1;min-width:0;';"
"bd.textContent=msg;el.appendChild(bd);"
"if(act&&act.label){"
"var btn=document.createElement('button');"
"btn.textContent=act.label;"
"btn.style.cssText='padding:4px 12px;border:1px solid var(--chandra-border,#333);border-radius:var(--chandra-radius,4px);background:transparent;color:var(--chandra-primary,#64B5F6);cursor:pointer;font-size:0.85em;flex-shrink:0;';"
"btn.onclick=function(e){e.stopPropagation();if(act.handler)window.chandra.invoke(act.handler,[]);dismiss(id);};"
"el.appendChild(btn);}"
"var cl=document.createElement('span');"
"cl.textContent='\\u00D7';"
"cl.style.cssText='cursor:pointer;opacity:0.5;font-size:1.2em;flex-shrink:0;padding:0 2px;';"
"cl.onclick=function(e){e.stopPropagation();dismiss(id);};"
"el.appendChild(cl);"
"el.onclick=function(){dismiss(id);};"
"while(c.children.length>=5){var ol=c.firstChild;if(ol)dismiss(ol.id);}"
"c.appendChild(el);t[id]=el;"
"requestAnimationFrame(function(){el.style.opacity='1';el.style.transform='translateX(0)';});"
"if(dur>0)setTimeout(function(){dismiss(id);},dur);}"
"function dismiss(id){"
"var el=t[id];if(!el)return;"
"el.style.opacity='0';el.style.transform='translateX(100%)';"

Chandra.xs  view on Meta::CPAN

"modal.style.cssText='background:var(--chandra-bg,#fff);border:1px solid var(--chandra-border,#e0e0e0);border-radius:var(--chandra-radius,6px);box-shadow:var(--chandra-shadow,0 4px 16px rgba(0,0,0,0.2));padding:0;min-width:320px;max-width:'+(opts.wid...
/* Title bar */
"if(opts.title){"
"var hdr=document.createElement('div');"
"hdr.style.cssText='padding:16px 20px 12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--chandra-border,#e0e0e0);';"
"var t=document.createElement('h3');"
"t.style.cssText='margin:0;font-size:1.1em;';t.textContent=opts.title;"
"hdr.appendChild(t);"
"if(opts.closable!==false){"
"var x=document.createElement('span');"
"x.textContent='\\u00D7';x.style.cssText='cursor:pointer;font-size:1.4em;opacity:0.5;padding:0 4px;';"
"x.onclick=function(){close(id);};hdr.appendChild(x);}"
"modal.appendChild(hdr);}"
/* Body */
"var body=document.createElement('div');"
"body.style.cssText='padding:16px 20px;';body.id=id+'_body';"
"if(opts.content)body.innerHTML=opts.content;"
"if(opts.message){var p=document.createElement('p');p.style.margin='0';p.textContent=opts.message;body.appendChild(p);}"
"if(opts.input){"
"var inp=document.createElement('input');"
"inp.type='text';inp.id=id+'_input';"

examples/assets_mount_example.pl  view on Meta::CPAN

    background: rgba(255,255,255,0.15);
    backdrop-filter: blur(10px);
    border-radius: 12px;
    padding: 1.5em;
    margin: 1em 0;
    border: 1px solid rgba(255,255,255,0.2);
}
button {
    background: #fff; color: #764ba2;
    border: none; border-radius: 6px;
    padding: 0.6em 1.2em; cursor: pointer;
    font-weight: bold; font-size: 1em;
}
button:hover { opacity: 0.9; }
#status { color: #a5d6a7; font-weight: bold; }
CSS

_write("$asset_dir/js/main.js", <<'JS');
(function init() {
    // Protocol-loaded scripts arrive after DOMContentLoaded,
    // so run immediately when the DOM is already ready.

examples/bind_example.pl  view on Meta::CPAN

            padding: 15px;
            margin: 10px 0;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        button {
            background: rgb(76, 175, 80);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px;
        }
        button:hover { background: rgb(69, 160, 73); }
        input {
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            margin: 5px;
        }
        #result {

examples/bridge_extension_example.pl  view on Meta::CPAN

    h1 { text-align: center; margin-bottom: 16px; font-size: 22px; color: #e94560; }
    .input-row {
        display: flex; gap: 8px; margin-bottom: 16px;
    }
    .input-row input {
        flex: 1; padding: 10px 14px; border-radius: 8px; border: 1px solid #333;
        background: #1a1a2e; color: #fff; font-size: 14px; outline: none;
    }
    .input-row input:focus { border-color: #e94560; }
    .input-row button, #stats-btn {
        padding: 10px 18px; border-radius: 8px; border: none; cursor: pointer;
        background: #e94560; color: #fff; font-size: 14px; font-weight: 600;
        transition: background 0.2s;
    }
    .input-row button:hover, #stats-btn:hover { background: #c73652; }
    ul#tasks { list-style: none; }
    ul#tasks li {
        display: flex; align-items: center; gap: 8px;
        padding: 10px 14px; margin-bottom: 6px; border-radius: 8px;
        background: rgba(255,255,255,0.06); transition: opacity 0.3s;
    }
    ul#tasks li .text { flex: 1; }
    ul#tasks li .time { font-size: 11px; color: #888; }
    ul#tasks li button {
        padding: 4px 12px; border-radius: 6px; border: none;
        background: #2ecc71; color: #fff; cursor: pointer; font-size: 12px;
    }
    ul#tasks li button:disabled { background: #555; cursor: default; }
    ul#tasks li.done .text { text-decoration: line-through; color: #666; }
    #toasts {
        position: fixed; bottom: 12px; right: 12px; z-index: 999;
        display: flex; flex-direction: column; gap: 6px;
    }
    .toast {
        padding: 8px 16px; border-radius: 8px; font-size: 13px;
        animation: fadeIn 0.3s ease;
    }
    .toast-info    { background: #2980b9; color: #fff; }

examples/canvas_demo.pl  view on Meta::CPAN

    .canvas-wrapper {
        background: #fff;
        border-radius: 12px;
        padding: 10px;
        box-shadow: 0 10px 40px rgba(0,0,0,0.3);
        margin-bottom: 20px;
    }
    canvas {
        display: block;
        border-radius: 8px;
        cursor: crosshair;
    }
    .toolbar {
        display: flex;
        gap: 15px;
        flex-wrap: wrap;
        justify-content: center;
        margin-bottom: 15px;
    }
    .tool-group {
        background: rgba(255,255,255,0.1);

examples/canvas_demo.pl  view on Meta::CPAN

        font-size: 12px;
        color: #aaa;
        margin-right: 5px;
    }
    button {
        background: #3498db;
        color: #fff;
        border: none;
        padding: 8px 16px;
        border-radius: 6px;
        cursor: pointer;
        font-size: 13px;
        transition: all 0.2s;
    }
    button:hover {
        background: #2980b9;
        transform: translateY(-1px);
    }
    button.active {
        background: #e74c3c;
    }

examples/canvas_demo.pl  view on Meta::CPAN

        background: #27ae60;
    }
    button.demo:hover {
        background: #219a52;
    }
    .color-btn {
        width: 28px;
        height: 28px;
        border-radius: 50%;
        border: 2px solid transparent;
        cursor: pointer;
        transition: all 0.2s;
    }
    .color-btn:hover {
        transform: scale(1.1);
    }
    .color-btn.active {
        border-color: #fff;
        box-shadow: 0 0 10px rgba(255,255,255,0.5);
    }
    .status {

examples/chat_window.pl  view on Meta::CPAN

        outline: none;
    }
    #input:focus { border-color: #e94560; }
    #send-btn {
        padding: 8px 20px;
        border-radius: 20px;
        border: none;
        background: #e94560;
        color: #fff;
        font-size: 14px;
        cursor: pointer;
    }
    #send-btn:hover { background: #c83b54; }
</style>
</head>
<body>
    <div id="header">
        <span>Chat &mdash; <span class="name">$name</span></span>
        <span id="users"></span>
    </div>
    <div id="messages"></div>

examples/contextmenu_example.pl  view on Meta::CPAN

<head>
<style>
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
           margin: 0; display: flex; flex-direction: column; height: 100vh;
           background: #f5f5f5; color: #333; }
    .header { padding: 12px 16px; background: #2c3e50; color: white;
              font-size: 14px; font-weight: 600; }
    .main { display: flex; flex: 1; overflow: hidden; }
    .sidebar { width: 200px; background: #ecf0f1; border-right: 1px solid #ddd;
               padding: 8px 0; overflow-y: auto; }
    .list-item { padding: 8px 16px; cursor: pointer; font-size: 13px; }
    .list-item:hover { background: #dfe6e9; }
    #editor { flex: 1; padding: 16px; font-family: monospace; font-size: 13px;
              white-space: pre-wrap; background: #fff; overflow-y: auto;
              line-height: 1.6; min-height: 200px; }
    #log { height: 120px; overflow-y: auto; background: #2d2d2d; color: #0f0;
           font-family: monospace; font-size: 12px; padding: 8px; border-top: 2px solid #444; }
    .log-entry { padding: 2px 0; }
    .hint { color: #888; font-size: 12px; padding: 8px 16px; background: #fafafa;
            border-top: 1px solid #eee; }
</style>

examples/counter_app.pl  view on Meta::CPAN

    }
    #count.bump { transform: scale(1.2); }
    .buttons { display: flex; gap: 12px; justify-content: center; margin-top: 20px; }
    button {
        width: 50px; height: 50px;
        border-radius: 50%;
        border: 2px solid rgba(255,255,255,0.5);
        background: rgba(255,255,255,0.2);
        color: #fff;
        font-size: 24px;
        cursor: pointer;
        transition: all 0.15s ease;
    }
    button:hover { background: rgba(255,255,255,0.35); transform: scale(1.1); }
    button:active { transform: scale(0.95); }
    .reset {
        margin-top: 16px;
        width: auto;
        border-radius: 8px;
        padding: 8px 24px;
        font-size: 14px;

examples/devtools_example.pl  view on Meta::CPAN


my $ui = Chandra::Element->new({
    tag => 'div',
    style => { padding => '20px', 'font-family' => 'sans-serif' },
    children => [
        { tag => 'h1', data => 'DevTools Demo' },
        { tag => 'p', data => 'Press F12 to open the DevTools panel.' },
        {
            tag     => 'button',
            data    => 'Trigger Error',
            style   => 'padding:8px 16px;margin:4px;cursor:pointer;',
            onclick => sub {
                my ($event, $app) = @_;
                $app->eval_js("window.chandra.invoke('trigger_error', [])");
            },
        },
        {
            tag     => 'button',
            data    => 'Say Hello',
            style   => 'padding:8px 16px;margin:4px;cursor:pointer;',
            onclick => sub {
                my ($event, $app) = @_;
                $app->eval_js("window.chandra.invoke('greet', ['World']).then(function(r){ document.getElementById('output').textContent = r; })");
            },
        },
        { tag => 'p', id => 'output', data => '', style => 'color:#333;margin-top:12px;' },
    ],
});

$app->set_content($ui);

examples/dialog_example.pl  view on Meta::CPAN

        margin-bottom: 12px;
        text-transform: uppercase;
        letter-spacing: 0.5px;
    }
    button {
        background: #3498db;
        color: #fff;
        border: none;
        padding: 10px 16px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin-right: 8px;
        margin-bottom: 8px;
    }
    button:hover {
        background: #2980b9;
    }
    button.warning {
        background: #f39c12;
    }

examples/dragdrop_example.pl  view on Meta::CPAN

    #upload-zone { min-height: 80px; }
    #upload-status { color: #4ecca3; margin-top: 8px; }

    .columns { display: flex; gap: 16px; margin-top: 12px; }
    .column {
        flex: 1; background: #16213e; border-radius: 8px;
        padding: 12px; min-height: 100px;
    }
    .draggable {
        background: #0f3460; padding: 8px 12px; border-radius: 6px;
        margin: 6px 0; cursor: grab; user-select: none;
    }
    .draggable:active { cursor: grabbing; }

    .log { max-height: 120px; overflow-y: auto; margin-top: 8px; }
    .entry { padding: 4px 0; border-bottom: 1px solid #333; font-size: 13px; }
</style>
</head>
<body>
    <h1>Drag &amp; Drop</h1>

    <h2>File Drop</h2>
    <div id="upload-zone" class="drop-zone">

examples/element_example.pl  view on Meta::CPAN

                    {
                        tag         => 'input',
                        type        => 'text',
                        id          => 'new-item',
                        placeholder => 'Add item...',
                        style       => 'flex:1; padding:8px; border:1px solid #ccc; border-radius:4px; font-size:14px',
                    },
                    {
                        tag     => 'button',
                        data    => 'Add',
                        style   => 'padding:8px 20px; background:#4CAF50; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:14px',
                        onclick => sub {
                            $app->dispatch_eval(
                                "var v=document.getElementById('new-item').value;" .
                                "if(v){window.chandra.invoke('add_item',[v]).then(function(){" .
                                "document.getElementById('new-item').value='';});}"
                            );
                        },
                    },
                ],
            },

examples/element_example.pl  view on Meta::CPAN

                my $item = $_;
                {
                    tag   => 'li',
                    id    => "item-$item->{id}",
                    style => 'display:flex; justify-content:space-between; align-items:center; padding:10px; margin:4px 0; background:#f9f9f9; border-radius:4px',
                    children => [
                        { tag => 'span', data => $item->{name}, style => 'font-size:14px' },
                        {
                            tag   => 'button',
                            data  => 'x',
                            style => 'background:#e74c3c; color:#fff; border:none; border-radius:50%; width:24px; height:24px; cursor:pointer; font-size:12px',
                            onclick => sub {
                                $app->dispatch_eval(
                                    "window.chandra.invoke('remove_item',[$item->{id}])"
                                );
                            },
                        },
                    ],
                }
            } @items
        ],

examples/form_example.pl  view on Meta::CPAN

  fieldset.chandra-group {
    border: 1px solid #444; border-radius: 6px;
    padding: 12px 14px; margin: 16px 0;
  }
  fieldset.chandra-group legend {
    font-size: 14px; font-weight: 600; padding: 0 6px;
  }
  .chandra-submit {
    padding: 10px 28px; font-size: 14px; font-weight: 600;
    background: #0078d4; color: #fff; border: none;
    border-radius: 4px; cursor: pointer; margin-top: 8px;
  }
  .chandra-submit:hover { background: #106ebe; }
  .chandra-error {
    display: block; font-size: 12px; color: #f44;
    min-height: 16px; margin-top: 2px;
  }
  #result {
    display: none; margin-top: 12px; padding: 10px;
    background: #1b5e20; border-radius: 4px;
    font-size: 14px; text-align: center;

examples/multiwindow_example.pl  view on Meta::CPAN

    my $del_btn     = $is_dark ? '#555'    : '#ccc';

    return qq(
<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family: system-ui, sans-serif; margin: 0; background: $bg; color: $li_fg; }
  h1   { background: #e74c3c; color: #fff; margin: 0; padding: 16px 20px; font-size: 1.4em; }
  .toolbar { display:flex; gap:8px; padding:12px 20px; background:$toolbar_bg; border-bottom:1px solid $toolbar_bdr; }
  button   { cursor:pointer; border:none; border-radius:4px; padding:6px 12px; font-size:.9em; }
  .btn-add { background:#e74c3c; color:#fff; }
  .btn-settings { background:$btn_bg; color:$btn_fg; margin-left:auto; }
  .filters { display:flex; gap:4px; }
  .filters button { background:$btn_bg; color:$btn_fg; }
  .filters button.active { background:#3498db; color:#fff; }
  ul   { list-style:none; margin:0; padding:0 20px; }
  li   { display:flex; align-items:center; gap:8px; padding:10px 0;
         border-bottom:1px solid $li_border; }
  li input[type=checkbox] { width:18px; height:18px; cursor:pointer; }
  li span { flex:1; }
  li button { background:transparent; color:$del_btn; font-size:1.1em; padding:2px 6px; }
  li button:hover { color:#e74c3c; }
  .footer { padding:12px 20px; color:$footer_fg; font-size:.85em; }
</style>
</head>
<body>
<h1>Todos</h1>
<div class="toolbar">
  <button class="btn-add"

examples/multiwindow_example.pl  view on Meta::CPAN

    return qq(
<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family:system-ui,sans-serif; margin:0; padding:20px; background:$bg; color:$fg; }
  h2   { margin-top:0; font-size:1.1em; }
  input[type=text] { width:100%; box-sizing:border-box; padding:8px; font-size:1em;
                     border:1px solid $input_bd; border-radius:4px; background:$input_bg; color:$fg; }
  .buttons { display:flex; gap:8px; margin-top:12px; justify-content:flex-end; }
  button { border:none; border-radius:4px; padding:8px 16px; cursor:pointer; font-size:.9em; }
  .ok     { background:#e74c3c; color:#fff; }
  .cancel { background:$btn_bg; color:$btn_fg; }
</style>
</head>
<body>
<h2>New Todo</h2>
<input type="text" id="txt" placeholder="What needs to be done?"
  onkeydown="if(event.key==='Enter') document.getElementById('ok').click()">
<div class="buttons">
  <button class="cancel"

examples/multiwindow_example.pl  view on Meta::CPAN

    my $fg     = $is_dark ? '#eee'    : '#333';
    
    return qq(
<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family:system-ui,sans-serif; margin:0; padding:20px; background:$bg; color:$fg; }
  h2   { margin-top:0; font-size:1.1em; }
  label { display:flex; align-items:center; gap:8px; margin:10px 0; }
  button { border:none; border-radius:4px; padding:8px 16px; cursor:pointer;
           background:#e74c3c; color:#fff; margin-top:12px; font-size:.9em; }
</style>
</head>
<body>
<h2>Settings</h2>
<label>
  <input type="checkbox" id="dark" $checked
    onchange="window.chandra.invoke('set_theme',[this.checked?'dark':'light'])">
  Dark theme
</label>

examples/pack_example/lib/PackedCounter.pm  view on Meta::CPAN

    }
    #count.bump { transform: scale(1.2); }
    .buttons { display: flex; gap: 12px; justify-content: center; margin-top: 20px; }
    button {
        width: 50px; height: 50px;
        border-radius: 50%;
        border: 2px solid rgba(255,255,255,0.5);
        background: rgba(255,255,255,0.2);
        color: #fff;
        font-size: 24px;
        cursor: pointer;
        transition: all 0.15s ease;
    }
    button:hover { background: rgba(255,255,255,0.35); transform: scale(1.1); }
    button:active { transform: scale(0.95); }
    .reset {
        margin-top: 16px;
        width: auto;
        border-radius: 8px;
        padding: 8px 24px;
        font-size: 14px;

include/chandra/chandra_devtools.h  view on Meta::CPAN

    "            header.appendChild(title);\n"
    "\n"
    "            var tabs = ['Console', 'Bindings', 'Elements'];\n"
    "            var self = this;\n"
    "            tabs.forEach(function(tab) {\n"
    "                var btn = document.createElement('button');\n"
    "                btn.textContent = tab;\n"
    "                btn.className = '__cdt-tab';\n"
    "                btn.setAttribute('data-tab', tab.toLowerCase());\n"
    "                btn.style.cssText = 'background:none;border:1px solid #45475a;color:#cdd6f4;' +\n"
    "                    'padding:2px 8px;margin:0 2px;cursor:pointer;border-radius:3px;font-size:11px;';\n"
    "                btn.onclick = function() { self.showTab(tab.toLowerCase()); };\n"
    "                header.appendChild(btn);\n"
    "            });\n"
    "\n"
    "            var reloadBtn = document.createElement('button');\n"
    "            reloadBtn.textContent = '\\u27F3 Reload';\n"
    "            reloadBtn.style.cssText = 'background:none;border:1px solid #45475a;color:#a6e3a1;' +\n"
    "                'padding:2px 8px;margin:0 2px;cursor:pointer;border-radius:3px;font-size:11px;';\n"
    "            reloadBtn.onclick = function() {\n"
    "                if (window.chandra) window.chandra.invoke('__devtools_reload', []);\n"
    "            };\n"
    "            header.appendChild(reloadBtn);\n"
    "\n"
    "            var clearBtn = document.createElement('button');\n"
    "            clearBtn.textContent = '\\u2718 Clear';\n"
    "            clearBtn.style.cssText = 'background:none;border:1px solid #45475a;color:#f9e2af;' +\n"
    "                'padding:2px 8px;margin:0 2px;cursor:pointer;border-radius:3px;font-size:11px;';\n"
    "            clearBtn.onclick = function() { self.clearConsole(); };\n"
    "            header.appendChild(clearBtn);\n"
    "\n"
    "            var closeBtn = document.createElement('button');\n"
    "            closeBtn.textContent = '\\u2715';\n"
    "            closeBtn.style.cssText = 'background:none;border:none;color:#f38ba8;padding:2px 6px;' +\n"
    "                'cursor:pointer;font-size:14px;margin-left:4px;';\n"
    "            closeBtn.onclick = function() { self.toggle(); };\n"
    "            header.appendChild(closeBtn);\n"
    "\n"
    "            this.panel.appendChild(header);\n"
    "\n"
    "            this.content = document.createElement('div');\n"
    "            this.content.style.cssText = 'height:calc(100% - 30px);overflow-y:auto;padding:8px;';\n"
    "            this.panel.appendChild(this.content);\n"
    "\n"
    "            document.body.appendChild(this.panel);\n"

lib/Chandra/Breadcrumb.pm  view on Meta::CPAN

sub css {
    return <<'CSS';
.chandra-breadcrumb-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; align-items: center; gap: 0;
    font-size: 0.9em;
}
.chandra-breadcrumb-item { display: flex; align-items: center; }
.chandra-breadcrumb-link {
    color: var(--chandra-primary, #2196F3);
    text-decoration: none; cursor: pointer;
}
.chandra-breadcrumb-link:hover { text-decoration: underline; }
.chandra-breadcrumb-current { color: var(--chandra-text-muted, #757575); }
.chandra-breadcrumb-sep {
    margin: 0 8px;
    color: var(--chandra-text-muted, #999);
}
CSS
}

lib/Chandra/Nav.pm  view on Meta::CPAN

}
.chandra-nav-sidebar.chandra-nav-collapsed .chandra-nav-label,
.chandra-nav-sidebar.chandra-nav-collapsed .chandra-nav-badge { display: none; }
.chandra-nav-sidebar.chandra-nav-collapsed .chandra-nav-link {
    justify-content: center;
    padding: 10px 0;
}
.chandra-nav-sidebar .chandra-nav-link {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 16px; color: var(--chandra-text, #212121);
    text-decoration: none; cursor: pointer;
    transition: background 0.15s;
}
.chandra-nav-sidebar .chandra-nav-link:hover { background: var(--chandra-hover, #e8e8e8); }
.chandra-nav-sidebar .chandra-nav-active .chandra-nav-link {
    background: var(--chandra-selected, #e3f2fd);
    color: var(--chandra-primary, #2196F3);
    font-weight: 600;
}
.chandra-nav-separator { border-top: 1px solid var(--chandra-border, #e0e0e0); margin: 4px 12px; }
.chandra-nav-toggle {
    width: 52px; border: none;
    background: transparent;
    font-size: 1.1em; cursor: pointer;
    color: var(--chandra-text-muted, #757575);
    flex-shrink: 0;
    text-align: center;
}
.chandra-nav-icon { font-size: 1.1em; flex-shrink: 0; width: 24px; text-align: center; }
.chandra-nav-badge {
    margin-left: auto; background: var(--chandra-primary, #2196F3);
    color: #fff; padding: 1px 8px; border-radius: 10px; font-size: 0.75em;
}

/* Topbar */
.chandra-nav-topbar {
    background: var(--chandra-surface, #f5f5f5);
    border-bottom: 1px solid var(--chandra-border, #e0e0e0);
    padding: 0 8px;
}
.chandra-nav-topbar .chandra-nav-list { display: flex; gap: 0; }
.chandra-nav-topbar .chandra-nav-link {
    display: flex; align-items: center; gap: 6px;
    padding: 12px 16px; color: var(--chandra-text, #212121);
    text-decoration: none; cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: border-color 0.15s, color 0.15s;
}
.chandra-nav-topbar .chandra-nav-link:hover { color: var(--chandra-primary, #2196F3); }
.chandra-nav-topbar .chandra-nav-active .chandra-nav-link {
    color: var(--chandra-primary, #2196F3);
    border-bottom-color: var(--chandra-primary, #2196F3);
    font-weight: 600;
}
.chandra-nav-topbar .chandra-nav-separator { display: none; }

lib/Chandra/Table.pm  view on Meta::CPAN

}

# ── CSS ────────────────────────────────────────────────────

sub css {
    return <<'CSS';
.chandra-table-wrap { font-family: system-ui, -apple-system, sans-serif; }
.chandra-table { width: 100%; border-collapse: collapse; }
.chandra-table th, .chandra-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #e0e0e0; }
.chandra-table th { background: #f5f5f5; font-weight: 600; user-select: none; }
.chandra-table-sortable { cursor: pointer; }
.chandra-table-sortable:hover { background: #e8e8e8; }
.chandra-table-stripe { background: #fafafa; }
.chandra-table-selected { background: #e3f2fd !important; }
.chandra-table-select { width: 40px; text-align: center; }
.chandra-table-empty, .chandra-table-loading { text-align: center; padding: 24px; color: #999; }
.chandra-table-filters { padding: 8px 0; display: flex; gap: 8px; flex-wrap: wrap; }
.chandra-table-filter { padding: 4px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; }
.chandra-table-pagination { display: flex; align-items: center; justify-content: center; gap: 4px; padding: 12px 0; }
.chandra-table-page-btn { padding: 4px 10px; border: 1px solid #ddd; border-radius: 4px; background: #fff; cursor: pointer; font-size: 13px; }
.chandra-table-page-btn:hover { background: #f0f0f0; }
.chandra-table-page-active { background: #2196F3 !important; color: #fff; border-color: #2196F3; }
.chandra-table-info { margin-right: 12px; font-size: 13px; color: #666; }
CSS
}

1;

__END__

lib/Chandra/Tabs.pm  view on Meta::CPAN

.chandra-tabs-header {
    display: flex;
    border-bottom: 1px solid var(--chandra-border, #e0e0e0);
    gap: 0;
}
.chandra-tab {
    padding: 10px 20px;
    border: none;
    background: transparent;
    color: var(--chandra-text-muted, #757575);
    cursor: pointer;
    font-size: inherit;
    font-family: inherit;
    border-bottom: 2px solid transparent;
    transition: color 0.15s, border-color 0.15s;
    display: flex;
    align-items: center;
    gap: 6px;
}
.chandra-tab:hover { color: var(--chandra-text, #212121); }
.chandra-tab-active {

lib/Chandra/Theme.pm  view on Meta::CPAN

    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 16px;
    border: 1px solid var(--chandra-border);
    border-radius: var(--chandra-radius);
    background: var(--chandra-surface);
    color: var(--chandra-text);
    font-family: inherit;
    font-size: inherit;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
button:hover, .chandra-btn:hover { background: var(--chandra-hover); }
button:disabled { opacity: 0.5; cursor: not-allowed; }

.chandra-btn-primary {
    background: var(--chandra-primary);
    color: #fff;
    border-color: var(--chandra-primary);
}
.chandra-btn-primary:hover { opacity: 0.9; }

.chandra-btn-danger {
    background: var(--chandra-danger);

lib/Chandra/Theme.pm  view on Meta::CPAN

.chandra-form { display: flex; flex-direction: column; gap: 12px; }
.chandra-field { display: flex; flex-direction: column; gap: 4px; }
.chandra-label { font-weight: 500; font-size: 0.9em; color: var(--chandra-text-muted); }
.chandra-submit {
    align-self: flex-start;
    padding: 8px 24px;
    background: var(--chandra-primary);
    color: #fff;
    border: none;
    border-radius: var(--chandra-radius);
    cursor: pointer;
    font-weight: 500;
}
.chandra-submit:hover { opacity: 0.9; }
.chandra-error { color: var(--chandra-danger); font-size: 0.85em; }
.chandra-group { border: 1px solid var(--chandra-border); border-radius: var(--chandra-radius); padding: 12px; }
.chandra-group legend { font-weight: 600; padding: 0 6px; }

/* ── Table (Chandra::Table) ───────────────────────────── */

.chandra-table-wrap { font-family: inherit; }

lib/Chandra/Theme.pm  view on Meta::CPAN

.chandra-table th, .chandra-table td {
    padding: 8px 12px;
    text-align: left;
    border-bottom: 1px solid var(--chandra-border);
}
.chandra-table th {
    background: var(--chandra-surface);
    font-weight: 600;
    user-select: none;
}
.chandra-table-sortable { cursor: pointer; }
.chandra-table-sortable:hover { background: var(--chandra-hover); }
.chandra-table-stripe { background: var(--chandra-surface); }
.chandra-table-selected { background: var(--chandra-selected) !important; }
.chandra-table-select { width: 40px; text-align: center; }
.chandra-table-empty, .chandra-table-loading {
    text-align: center;
    padding: 24px;
    color: var(--chandra-text-muted);
}
.chandra-table-filters { padding: 8px 0; display: flex; gap: 8px; flex-wrap: wrap; }

lib/Chandra/Theme.pm  view on Meta::CPAN

.chandra-context-menu {
    background: var(--chandra-bg);
    border: 1px solid var(--chandra-border);
    border-radius: var(--chandra-radius);
    box-shadow: var(--chandra-shadow);
    padding: 4px 0;
    min-width: 180px;
}
.chandra-context-menu-item {
    padding: 6px 16px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
}
.chandra-context-menu-item:hover { background: var(--chandra-hover); }

/* ── Scrollbar (webkit) ───────────────────────────────── */

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }

xs/contextmenu.xs  view on Meta::CPAN

        ".chandra-ctx-menu{"
            "position:fixed;z-index:999999;min-width:160px;"
            "background:#fff;border:1px solid #ccc;border-radius:6px;"
            "box-shadow:0 4px 16px rgba(0,0,0,.18);padding:4px 0;"
            "font:13px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;"
            "color:#222;user-select:none;opacity:0;transform:scale(.96);"
            "transition:opacity .12s,transform .12s;"
        "}"
        ".chandra-ctx-menu.show{opacity:1;transform:scale(1);}"
        ".chandra-ctx-item{"
            "padding:6px 32px 6px 28px;cursor:pointer;white-space:nowrap;"
            "display:flex;align-items:center;position:relative;"
        "}"
        ".chandra-ctx-item:hover{background:#e8f0fe;}"
        ".chandra-ctx-item.disabled{color:#999;pointer-events:none;}"
        ".chandra-ctx-sep{height:1px;background:#e0e0e0;margin:4px 8px;}"
        ".chandra-ctx-icon{width:20px;margin-right:6px;text-align:center;}"
        ".chandra-ctx-sc{margin-left:auto;padding-left:24px;color:#888;font-size:12px;}"
        ".chandra-ctx-check{position:absolute;left:8px;}"
        ".chandra-ctx-sub-arrow{margin-left:auto;padding-left:16px;color:#888;}"
        ".chandra-ctx-sub{position:fixed;}"



( run in 1.189 second using v1.01-cache-2.11-cpan-140bd7fdf52 )