TNI TOOLKIT

Style Guide

← Back to TNI Toolkit

Complete styling reference for building TNI Toolkit tools. All tools use a Network Operations Center (NOC) aesthetic — dark theme, monospace typography, terminal-style green/blue accents.

🎨

Design Tokens

Colors - Background

/* Background */
--bg-gradient: linear-gradient(135deg, #0a0e14 0%, #1a1f2e 50%, #0d1117 100%);
--surface: rgba(22, 27, 34, 0.8);
--surface-dark: rgba(22, 27, 34, 0.6);

/* Borders */
--border: #30363d;
--border-light: rgba(48, 54, 61, 0.5);
--bg-gradient 135deg gradient
--surface rgba(22, 27, 34, 0.8)
--border #30363d

Colors - Text

/* Text */
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--text-muted: #7d8590;
--text-primary #c9d1d9
--text-secondary #8b949e
--text-muted #7d8590

Colors - Accents

/* Accent Colors */
--accent-green: #00ff88;          /* Primary action, success, selected */
--accent-green-dim: #238636;      /* Success backgrounds */
--accent-blue: #58a6ff;           /* Info, links, hover states */
--accent-orange: #f0883e;         /* Warnings, costs, attention */
--accent-red: #f85149;            /* Errors, destructive actions */

/* Transparent Variants (for backgrounds) */
--accent-green-bg: rgba(0, 255, 136, 0.08);
--accent-green-bg-hover: rgba(0, 255, 136, 0.15);
--accent-blue-bg: rgba(88, 166, 255, 0.08);
--accent-orange-bg: rgba(240, 136, 62, 0.08);
--accent-red-bg: rgba(248, 81, 73, 0.15);
--accent-green #00ff88
--accent-green-dim #238636
--accent-blue #58a6ff
--accent-orange #f0883e
--accent-red #f85149

Colors - Code Syntax

Syntax highlighting uses UI theme colors for consistency. All code blocks follow this scheme.

/* Syntax highlighting - uses UI theme colors */
.css-prop, .js-method, .html-attr { color: #58a6ff; }  /* Blue - properties/methods */
.css-val, .js-string, .html-tag { color: #00ff88; }   /* Green - values/strings/tags */
.js-keyword, .html-string { color: #f0883e; }         /* Orange - keywords/attr values */
.css-comment, .js-comment { color: #7d8590; }          /* Muted - comments */
Properties / Methods #58a6ff
Values / Strings / Tags #00ff88
Keywords / Attr Values #f0883e
Comments #7d8590

Typography

/* Font Stack */
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;

/* Scale */
--text-xs: 10px;    /* Fine print, metadata */
--text-sm: 11px;    /* Small labels, stats */
--text-base: 12px;  /* Body text */
--text-md: 13px;    /* Panel headers */
--text-lg: 14px;    /* Card titles */
--text-xl: 20px;    /* Page title */
--text-2xl: 28px;   /* Hero/logo */

/* Weights */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
SizeValueUsageExample
--text-xs10pxFine print, metadataSample text
--text-sm11pxSmall labels, statsSample text
--text-base12pxBody textSample text
--text-md13pxPanel headersHEADER
--text-lg14pxCard titlesCard Title
--text-xl20pxPage titleTITLE
--text-2xl28pxHero/logoTNI

Spacing, Borders & Effects

/* Spacing */
--space-1: 4px;   --space-2: 8px;
--space-3: 10px;  --space-4: 12px;
--space-5: 16px;  --space-6: 20px;
--space-7: 24px;  --space-8: 32px;

/* Border Radius */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;

/* Shadows & Effects */
--glow-green: 0 0 20px rgba(0, 255, 136, 0.3);
--glow-strong: 0 0 30px rgba(0, 255, 136, 0.4);

/* Transitions */
--transition-fast: 0.15s ease;
--transition-base: 0.2s ease;
Border Radius
4px / 6px / 8px
Glow Effects
glow / strong
📄

Base Template

📋 Start every new tool with this template

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TNI [Tool Name]</title>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        
        body {
            font-family: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;
            background: linear-gradient(135deg, #0a0e14 0%, #1a1f2e 50%, #0d1117 100%);
            color: #c9d1d9;
            min-height: 100vh;
            padding: 24px;
            line-height: 1.6;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        /* === HEADER === */
        .header {
            text-align: center;
            border-bottom: 1px solid #30363d;
            padding-bottom: 16px;
            margin-bottom: 24px;
        }
        
        h1 {
            color: #00ff88;
            text-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
            margin: 0 0 8px 0;
            font-size: 20px;
            font-weight: 600;
            letter-spacing: 2px;
            text-transform: uppercase;
        }
        
        h1 span { color: #58a6ff; }
        
        .subtitle { color: #8b949e; font-size: 12px; margin: 0; }
        .stats { color: #7d8590; font-size: 11px; margin-top: 8px; }
        
        /* === PANELS === */
        .panel {
            background: rgba(22, 27, 34, 0.8);
            border: 1px solid #30363d;
            border-radius: 6px;
            padding: 20px;
            margin-bottom: 16px;
        }
        
        /* Panel headings - no margin, use padding for border separation */
        .panel h2 {
            margin: 0;
            padding-bottom: 12px;
            border-bottom: 1px solid #30363d;
            color: #58a6ff;
            font-size: 13px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 1px;
            line-height: 1.3;
        }
        
        /* Spacing between elements via margin-top */
        .panel h2 + * { margin-top: 12px; }
        
        /* Remove bottom margin from last child for symmetric padding */
        .panel > *:last-child { margin-bottom: 0; }
        
        /* === FOOTER === */
        .site-footer {
            margin-top: 40px;
            padding-top: 16px;
            border-top: 1px solid #30363d;
            text-align: center;
            font-size: 11px;
            color: #7d8590;
        }
        
        .site-footer a {
            color: #8b949e;
            text-decoration: none;
            transition: color 0.15s;
        }
        
        .site-footer a: hover { color: #58a6ff; }
        .site-footer .sep { margin: 0 8px; color: #30363d; }
        
        /* === RESPONSIVE === */
        @media (max-width: 600px) {
            body { padding: 16px; }
        }
    </style>
</head>
<body>
    <div class="container">
        <header class="header">
            <h1>TNI <span>[TOOL NAME]</span></h1>
            <p class="subtitle">Brief description</p>
        </header>
        
        <div class="panel">
            <h2>📋 Section Title</h2>
            <!-- Content here -->
        </div>
        
        <footer class="site-footer">
            <a href="https://github.com/salvo-praxis/tni-toolkit">TNI Toolkit</a>
            <span class="sep">|</span>
            <a href="https://store.steampowered.com/app/2939600/">TNI on Steam</a>
        </footer>
    </div>
    
    <script>
        // Tool logic here
    </script>
</body>
</html>
📦

Master CSS

Complete, production-ready CSS for all TNI Toolkit components. Copy or download the entire stylesheet to use in new tools.

📋 TNI Toolkit Core Styles

/* ================================================================== */
/* TNI TOOLKIT - MASTER STYLESHEET                                    */
/* Version: 1.0.0                                                     */
/* Updated: 2025-12                                                   */
/* ================================================================== */

/* === RESET & BASE === */
* { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;
    background: linear-gradient(135deg, #0a0e14 0%, #1a1f2e 50%, #0d1117 100%);
    color: #c9d1d9;
    min-height: 100vh;
    padding: 24px;
    line-height: 1.6;
}

/* === LAYOUT === */
.container {
    max-width: 1200px;
    margin: 0 auto;
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}

.flex-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

/* === HEADER === */
.header {
    text-align: center;
    border-bottom: 1px solid #30363d;
    padding-bottom: 16px;
    margin-bottom: 24px;
}

h1 {
    color: #00ff88;
    text-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
    margin: 0 0 8px 0;
    font-size: 20px;
    font-weight: 600;
    letter-spacing: 2px;
    text-transform: uppercase;
}

h1 span { color: #58a6ff; }

.subtitle {
    color: #8b949e;
    font-size: 12px;
    margin: 0;
}

/* === PANELS === */
.panel {
    background: rgba(22, 27, 34, 0.8);
    border: 1px solid #30363d;
    border-radius: 6px;
    padding: 20px;
    margin-bottom: 16px;
}

.panel h2 {
    margin: 0;
    padding-bottom: 12px;
    border-bottom: 1px solid #30363d;
    color: #58a6ff;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    line-height: 1.3;
}

.panel h2 + * { margin-top: 12px; }
.panel > *:last-child { margin-bottom: 0; }

/* === CARDS === */
.card {
    background: rgba(22, 27, 34, 0.8);
    border: 1px solid #30363d;
    border-radius: 6px;
    padding: 12px;
    cursor: pointer;
    transition: all 0.15s;
}

.card:hover {
    border-color: #58a6ff;
    background: rgba(88, 166, 255, 0.08);
}

.card.selected {
    border-color: #00ff88;
    background: rgba(0, 255, 136, 0.1);
}

.card.disabled {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

.card-name {
    color: #c9d1d9;
    font-size: 12px;
    font-weight: 500;
}

.card-meta {
    color: #7d8590;
    font-size: 10px;
    margin-top: 4px;
}

/* === BUTTONS === */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border-radius: 4px;
    font-size: 11px;
    font-family: inherit;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    text-decoration: none;
}

.btn-primary {
    background: rgba(0, 255, 136, 0.15);
    border: 1px solid #00ff88;
    color: #00ff88;
}

.btn-primary:hover {
    background: rgba(0, 255, 136, 0.25);
}

.btn-secondary {
    background: rgba(88, 166, 255, 0.1);
    border: 1px solid #30363d;
    color: #8b949e;
}

.btn-secondary:hover {
    border-color: #58a6ff;
    color: #58a6ff;
}

.btn-danger {
    background: rgba(248, 81, 73, 0.15);
    border: 1px solid #f85149;
    color: #f85149;
}

.btn-danger:hover {
    background: rgba(248, 81, 73, 0.25);
}

/* === FORM INPUTS === */
input[type="text"],
input[type="search"],
input[type="number"] {
    width: 100%;
    padding: 10px 12px;
    border-radius: 6px;
    border: 1px solid #30363d;
    background: rgba(22, 27, 34, 0.8);
    color: #c9d1d9;
    font-size: 12px;
    font-family: inherit;
    transition: border-color 0.15s;
}

input:focus {
    outline: none;
    border-color: #58a6ff;
}

input::placeholder {
    color: #8b949e;
}

/* === CUSTOM DROPDOWN === */
.dropdown {
    position: relative;
}

.dropdown-selected {
    padding: 10px 12px;
    padding-right: 32px;
    border-radius: 6px;
    border: 1px solid #30363d;
    background: rgba(22, 27, 34, 0.8);
    color: #c9d1d9;
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s;
    position: relative;
}

.dropdown-selected::after {
    content: "▼";
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 8px;
    color: #8b949e;
    transition: transform 0.15s;
}

.dropdown-selected:hover {
    border-color: #58a6ff;
}

.dropdown.open .dropdown-selected {
    border-color: #58a6ff;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.dropdown.open .dropdown-selected::after {
    transform: translateY(-50%) rotate(180deg);
}

.dropdown-options {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: rgba(22, 27, 34, 0.95);
    border: 1px solid #58a6ff;
    border-top: none;
    border-bottom-left-radius: 6px;
    border-bottom-right-radius: 6px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 100;
    display: none;
}

.dropdown.open .dropdown-options {
    display: block;
}

.dropdown-option {
    padding: 10px 12px;
    font-size: 12px;
    color: #8b949e;
    cursor: pointer;
    transition: all 0.1s;
}

.dropdown-option:hover {
    background: rgba(88, 166, 255, 0.15);
    color: #c9d1d9;
}

.dropdown-option.selected {
    background: rgba(0, 255, 136, 0.15);
    color: #00ff88;
}

/* === TABLES === */
.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    line-height: 1.3;
}

.table th {
    text-align: left;
    color: #58a6ff;
    font-weight: 600;
    padding: 0 12px 10px 12px;
    border-bottom: 1px solid #30363d;
    font-size: 11px;
    text-transform: uppercase;
}

.table td {
    padding: 10px 12px;
    border-bottom: 1px solid rgba(48, 54, 61, 0.5);
    color: #8b949e;
}

.table tr:last-child td {
    border-bottom: none;
    padding-bottom: 0;
}

.table tr:hover td {
    background: rgba(88, 166, 255, 0.05);
}

/* === TAGS === */
.tag {
    background: rgba(0, 255, 136, 0.15);
    border: 1px solid #00ff88;
    color: #00ff88;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.tag .remove {
    cursor: pointer;
    opacity: 0.7;
}

.tag .remove:hover {
    opacity: 1;
}

/* === CALLOUTS === */
.callout {
    background: rgba(240, 136, 62, 0.08);
    border: 1px solid #f0883e;
    border-radius: 6px;
    padding: 16px;
}

.callout-header {
    color: #f0883e;
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 8px;
}

.callout-success {
    background: rgba(0, 255, 136, 0.08);
    border-color: #00ff88;
}

.callout-success .callout-header {
    color: #00ff88;
}

.callout-error {
    background: rgba(248, 81, 73, 0.08);
    border-color: #f85149;
}

.callout-error .callout-header {
    color: #f85149;
}

/* === PROGRESS / STAT BARS === */
.stat-bar {
    display: flex;
    align-items: center;
    gap: 10px;
}

.stat-label {
    font-size: 11px;
    color: #8b949e;
    min-width: 50px;
}

.stat-track {
    flex: 1;
    height: 8px;
    background: rgba(22, 27, 34, 0.8);
    border-radius: 4px;
    overflow: hidden;
}

.stat-fill {
    height: 100%;
    background: #00ff88;
    border-radius: 4px;
    transition: width 0.3s ease;
}

.stat-fill.warning { background: #f0883e; }
.stat-fill.danger { background: #f85149; }

.stat-value {
    font-size: 11px;
    color: #c9d1d9;
    min-width: 40px;
    text-align: right;
}

/* === TOOLTIP === */
.tooltip {
    position: fixed;
    background: #238636;
    color: #fff;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 1000;
}

.tooltip.show { opacity: 1; }

/* === EMPTY STATE === */
.empty-state {
    text-align: center;
    color: #8b949e;
    padding: 40px;
    font-size: 12px;
}

.empty-state small {
    display: block;
    margin-top: 8px;
    color: #7d8590;
}

/* === CODE DISPLAY === */
code {
    background: rgba(0, 0, 0, 0.3);
    padding: 2px 6px;
    border-radius: 3px;
    font-size: 11px;
    color: #00ff88;
}

.code-value {
    font-size: 1.4em;
    font-weight: 600;
    color: #00ff88;
    text-align: center;
    padding: 10px 20px;
    background: rgba(0, 255, 136, 0.08);
    border: 1px solid #238636;
    border-radius: 4px;
    letter-spacing: 4px;
    cursor: pointer;
    transition: background 0.15s;
}

.code-value:hover {
    background: rgba(0, 255, 136, 0.15);
}

.code-value.copied {
    background: rgba(35, 134, 54, 0.3);
}

/* === TOOLTIP === */
.tooltip {
    position: fixed;
    background: #238636;
    color: #fff;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 1000;
}

.tooltip.show {
    opacity: 1;
}

/* === CUSTOM SCROLLBAR === */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: rgba(22, 27, 34, 0.5);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb {
    background: #30363d;
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: #58a6ff;
}

/* === FOOTER === */
.site-footer {
    margin-top: 40px;
    padding-top: 16px;
    border-top: 1px solid #30363d;
    text-align: center;
    font-size: 11px;
    color: #7d8590;
}

.site-footer a {
    color: #8b949e;
    text-decoration: none;
    transition: color 0.15s;
}

.site-footer a: hover { color: #58a6ff; }
.site-footer .sep { margin: 0 8px; color: #30363d; }

/* === RESPONSIVE === */
@media (max-width: 600px) {
    body { padding: 16px; }
    .container { max-width: 100%; }
    .grid { grid-template-columns: 1fr; }
    h1 { font-size: 18px; }
}

Master JS

Complete JavaScript for all interactive TNI Toolkit components. Copy or download to add interactivity to your tools.

📋 TNI Toolkit Core Scripts

/* ================================================================== */
/* TNI TOOLKIT - MASTER JAVASCRIPT                                     */
/* Version: 1.0.0                                                      */
/* Updated: 2025-12                                                    */
/* ================================================================== */

/* === DROPDOWN === */
document.querySelectorAll('.dropdown').forEach(dropdown => {
    dropdown.addEventListener('click', () => {
        dropdown.classList.toggle('open');
    });
});

// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
    if (!e.target.closest('.dropdown')) {
        document.querySelectorAll('.dropdown.open')
            .forEach(d => d.classList.remove('open'));
    }
});

// Handle option selection
document.querySelectorAll('.dropdown-option').forEach(option => {
    option.addEventListener('click', (e) => {
        e.stopPropagation();
        const dropdown = option.closest('.dropdown');
        const selected = dropdown.querySelector('.dropdown-selected');
        selected.textContent = option.textContent;
        dropdown.querySelectorAll('.dropdown-option')
            .forEach(o => o.classList.remove('selected'));
        option.classList.add('selected');
        dropdown.classList.remove('open');
    });
});

/* === STEPPER CONTROL === */
function updateStepper(btn, delta) {
    const container = btn.parentElement;
    const valueEl = container.querySelector('.stepper-value');
    const parts = valueEl.textContent.split('/');
    let current = parseInt(parts[0]);
    const max = parseInt(parts[1]);
    current = Math.max(0, Math.min(max, current + delta));
    valueEl.textContent = current + '/' + max;
}

/* === COLLAPSIBLE PANELS === */
document.querySelectorAll('.collapsible-header').forEach(header => {
    header.addEventListener('click', function(e) {
        if (e.target.closest('.collapsible-copy')) return;
        const collapsible = this.closest('.collapsible');
        collapsible.classList.toggle('open');
    });
});

/* === COPY TO CLIPBOARD === */
function copyToClipboard(text, btn) {
    navigator.clipboard.writeText(text).then(() => {
        const originalText = btn.innerHTML;
        btn.innerHTML = '✓ Copied!';
        btn.classList.add('copied');
        setTimeout(() => {
            btn.innerHTML = originalText;
            btn.classList.remove('copied');
        }, 2000);
    });
}

// Collapsible section copy buttons
document.querySelectorAll('.collapsible-copy').forEach(btn => {
    btn.addEventListener('click', function(e) {
        e.stopPropagation();
        const codeEl = this.closest('.collapsible').querySelector('code');
        copyToClipboard(codeEl.textContent, this);
    });
});

/* === TOOLTIP === */
function showTooltip(x, y, text) {
    const tooltip = document.getElementById('tooltip');
    tooltip.textContent = text;
    tooltip.style.left = x + 'px';
    tooltip.style.top = y + 'px';
    tooltip.classList.add('show');
    setTimeout(() => tooltip.classList.remove('show'), 1500);
}

/* === COPYABLE VALUES === */
// Uses Tooltip (defined above)
function copyValue(el) {
    navigator.clipboard.writeText(el.textContent.trim()).then(() => {
        el.classList.add('copied');
        const tooltip = document.getElementById('tooltip');
        const rect = el.getBoundingClientRect();
        tooltip.style.left = rect.left + rect.width/2 - 40 + 'px';
        tooltip.style.top = rect.top - 40 + 'px';
        tooltip.classList.add('show');
        setTimeout(() => {
            el.classList.remove('copied');
            tooltip.classList.remove('show');
        }, 1500);
    });
}

/* === SELECTABLE CARDS === */
document.querySelectorAll('.card:not(.disabled)').forEach(card => {
    card.addEventListener('click', () => {
        card.classList.toggle('selected');
    });
});
📐

Layout Components

Container

.container {
    max-width: 1200px;  /* Or 1100px for narrower layouts */
    margin: 0 auto;
    padding: 24px;
}

Max-widths: Default tools: 1200px | Narrower/reading: 1100px | Compact: 900px

Grid Layouts

/* Auto-fill responsive grid */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 10px;
}

/* Larger cards */
.grid-lg {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 15px;
}

/* Two-column info layout */
.grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
}

Flex Layouts

/* Horizontal group with wrap */
.flex-row {
    display: flex;
    align-items: center;
    gap: 15px;
    flex-wrap: wrap;
}

/* Space between header */
.flex-between {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* Vertical stack */
.flex-col {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
🧩

UI Components

Panel (Container Card)

Main container for grouping content. Features border, padding, and optional header with bottom border.

Live Example

Panel Header

Panel content goes here. This is the standard container for toolkit sections.

CSS
.panel {
    background: rgba(22, 27, 34, 0.8);
    border: 1px solid #30363d;
    border-radius: 6px;
    padding: 20px;
    margin-bottom: 16px;
}

.panel h2 {
    margin: 0;
    padding-bottom: 12px;
    border-bottom: 1px solid #30363d;
    color: #58a6ff;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* Spacing between elements */
.panel h2 + * { margin-top: 12px; }

/* Symmetric padding */
.panel > *:last-child { margin-bottom: 0; }
HTML
<div class="panel">
    <h2>Panel Header</h2>
    <p>Panel content goes here.</p>
</div>

Interactive Card

Selectable cards with hover, selected, and disabled states. Used for equipment/option selection.

Live Example
Default
Hover me
Selected
💰 500
Disabled
Unavailable
CSS
.card {
    background: rgba(22, 27, 34, 0.8);
    border: 1px solid #30363d;
    border-radius: 6px;
    padding: 12px;
    cursor: pointer;
    transition: all 0.15s;
}

.card:hover {
    border-color: #58a6ff;
    background: rgba(88, 166, 255, 0.08);
}

.card.selected {
    border-color: #00ff88;
    background: rgba(0, 255, 136, 0.1);
}

.card.disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
HTML
<!-- Default card -->
<div class="card">
    <div class="card-name">Server Name</div>
    <div class="card-meta">💰 500</div>
</div>

<!-- Selected state -->
<div class="card selected">...</div>

<!-- Disabled state -->
<div class="card disabled">...</div>

Buttons

Action buttons in three variants: primary (green), secondary (subtle), and danger (red).

Live Example
CSS
/* Primary Button (Green) */
.btn-primary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border-radius: 4px;
    font-size: 11px;
    font-family: inherit;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    background: rgba(0, 255, 136, 0.15);
    border: 1px solid #00ff88;
    color: #00ff88;
}

.btn-primary:hover {
    background: rgba(0, 255, 136, 0.25);
    box-shadow: 0 0 12px rgba(0, 255, 136, 0.3);
}

/* Secondary Button (Subtle) */
.btn-secondary {
    background: rgba(88, 166, 255, 0.1);
    border: 1px solid #30363d;
    color: #8b949e;
}

.btn-secondary:hover {
    border-color: #58a6ff;
    color: #58a6ff;
}

/* Danger Button (Red) */
.btn-danger {
    background: rgba(248, 81, 73, 0.15);
    border: 1px solid #f85149;
    color: #f85149;
}

.btn-danger:hover {
    background: rgba(248, 81, 73, 0.25);
    box-shadow: 0 0 12px rgba(248, 81, 73, 0.3);
}
HTML
<button class="btn-primary">Primary Button</button>
<button class="btn-secondary">Secondary Button</button>
<button class="btn-danger">Danger Button</button>

Tags / Badges

Inline labels for selections, filters, or status indicators. Optional remove button for dismissible tags.

Live Example
Selected DNS-Lite Server 01
CSS
.tag {
    background: rgba(0, 255, 136, 0.15);
    border: 1px solid #00ff88;
    color: #00ff88;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.tag .remove {
    cursor: pointer;
    opacity: 0.7;
}

.tag .remove:hover {
    opacity: 1;
}
HTML
<!-- Simple tag -->
<span class="tag">Selected</span>

<!-- Dismissible tag -->
<span class="tag">
    DNS-Lite
    <span class="remove"></span>
</span>

Form Inputs

Text inputs and custom dropdowns. Native selects don't style well on dark themes, so we use custom dropdowns.

Live Example
Select server...
Server 01
Server 02
Server 03
CSS
/* Text Input */
input[type="text"] {
    width: 100%;
    padding: 10px 12px;
    border-radius: 6px;
    border: 1px solid #30363d;
    background: rgba(22, 27, 34, 0.8);
    color: #c9d1d9;
    font-size: 12px;
    font-family: inherit;
}

input:focus {
    outline: none;
    border-color: #58a6ff;
}

/* Custom Dropdown Container */
.dropdown {
    position: relative;
}

.dropdown-selected {
    padding: 10px 32px 10px 12px;
    border: 1px solid #30363d;
    border-radius: 6px;
    background: rgba(22, 27, 34, 0.8);
    color: #c9d1d9;
    cursor: pointer;
}

.dropdown-selected::after {
    content: "▼";
    position: absolute;
    right: 12px;
    color: #8b949e;
}

.dropdown-options {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: rgba(22, 27, 34, 0.95);
    border: 1px solid #58a6ff;
    border-top: none;
    max-height: 150px;
    overflow-y: auto;
    display: none;
}

.dropdown.open .dropdown-options {
    display: block;
}

.dropdown-option {
    padding: 10px 12px;
    cursor: pointer;
}

.dropdown-option:hover {
    background: rgba(88, 166, 255, 0.15);
}

.dropdown-option.selected {
    background: rgba(0, 255, 136, 0.15);
    color: #00ff88;
}
HTML
<!-- Text input -->
<input type="text" placeholder="Enter text...">

<!-- Custom dropdown -->
<div class="dropdown">
    <div class="dropdown-selected">Select option...</div>
    <div class="dropdown-options">
        <div class="dropdown-option">Option 1</div>
        <div class="dropdown-option selected">Option 2</div>
        <div class="dropdown-option">Option 3</div>
    </div>
</div>
JavaScript
// Toggle dropdown on click
document.querySelectorAll('.dropdown').forEach(dropdown => {
    dropdown.addEventListener('click', () => {
        dropdown.classList.toggle('open');
    });
});

// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
    if (!e.target.closest('.dropdown')) {
        document.querySelectorAll('.dropdown.open')
            .forEach(d => d.classList.remove('open'));
    }
});

// Handle option selection
document.querySelectorAll('.dropdown-option').forEach(option => {
    option.addEventListener('click', (e) => {
        e.stopPropagation();
        const dropdown = option.closest('.dropdown');
        const selected = dropdown.querySelector('.dropdown-selected');
        selected.textContent = option.textContent;
        dropdown.querySelectorAll('.dropdown-option')
            .forEach(o => o.classList.remove('selected'));
        option.classList.add('selected');
        dropdown.classList.remove('open');
    });
});

Stepper Control

Numeric input with −/+ buttons. Used for SATA expansion slots, quantities, etc.

Live Example
SATA Expansion
2/4
+8 Storage each
Disabled State
0/0
(no device selected)
CSS
/* Stepper Container */
.stepper {
    display: flex;
    align-items: center;
    gap: 0;
    background: #21262d;
    border-radius: 4px;
    overflow: hidden;
    border: 1px solid #30363d;
}

.stepper button {
    width: 28px;
    height: 26px;
    border: none;
    background: linear-gradient(180deg, #2d333b 0%, #22272e 100%);
    color: #58a6ff;
    font-size: 16px;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.15s;
}

.stepper button:hover:not(:disabled) {
    background: linear-gradient(180deg, #3d444d 0%, #2d333b 100%);
    color: #00ff88;
}

.stepper button:disabled {
    color: #484f58;
    cursor: not-allowed;
    background: #21262d;
}

.stepper-value {
    min-width: 40px;
    text-align: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    color: #00ff88;
    padding: 0 6px;
    background: rgba(0, 255, 136, 0.05);
}

/* Optional: Row wrapper with label */
.stepper-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: rgba(22, 27, 34, 0.8);
    border: 1px solid #30363d;
    border-radius: 6px;
}
HTML
<div class="stepper-row">
    <span class="stepper-label">Quantity</span>
    <div class="stepper">
        <button class="stepper-minus"></button>
        <span class="stepper-value">3</span>
        <button class="stepper-plus">+</button>
    </div>
    <span class="stepper-info">(0-10 available)</span>
</div>
JavaScript
function updateStepper(btn, delta) {
    const container = btn.parentElement;
    const valueEl = container.querySelector('.stepper-value');
    const parts = valueEl.textContent.split('/');
    let current = parseInt(parts[0]);
    const max = parseInt(parts[1]);
    current = Math.max(0, Math.min(max, current + delta));
    valueEl.textContent = current + '/' + max;
}

Collapsible Panel

Expandable sections for organizing code examples. Click header to toggle, copy button copies code without toggling.

Live Example
Example Section (Expanded)
// This section starts expanded (has .open class)
// Click the header to collapse it
console.log("Hello from the collapsible panel!");

👇 Expand the code sections below to see how it's built.

CSS
/* Collapsible section */
.collapsible {
    border: 1px solid #30363d;
    border-radius: 6px;
    margin-bottom: 8px;
    overflow: hidden;
}

.collapsible-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: rgba(0, 0, 0, 0.3);
    cursor: pointer;
    user-select: none;
    transition: background 0.15s;
}

.collapsible-header:hover {
    background: rgba(0, 0, 0, 0.5);
}

.collapsible-arrow {
    color: #8b949e;
    font-size: 10px;
    transition: transform 0.2s;
    width: 12px;
}

.collapsible.open .collapsible-arrow {
    transform: rotate(90deg);
}

.collapsible-title {
    color: #c9d1d9;
    font-size: 11px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.collapsible-spacer { flex: 1; }

.collapsible-copy {
    padding: 4px 8px;
    background: rgba(88, 166, 255, 0.1);
    border: 1px solid #30363d;
    border-radius: 4px;
    color: #8b949e;
    font-size: 10px;
    cursor: pointer;
    transition: all 0.15s;
}

.collapsible-copy:hover {
    border-color: #58a6ff;
    color: #58a6ff;
}

.collapsible-copy.copied {
    border-color: #00ff88;
    color: #00ff88;
}

.collapsible-content {
    display: none;
    border-top: 1px solid #30363d;
}

.collapsible.open .collapsible-content {
    display: block;
}

.collapsible-content pre {
    margin: 0;
    border: none;
    border-radius: 0;
    max-height: 250px;
    overflow-y: auto;
}
HTML
<div class="collapsible">
    <div class="collapsible-header">
        <span class="collapsible-arrow"></span>
        <span class="collapsible-title">Section Title</span>
        <span class="collapsible-spacer"></span>
        <button class="collapsible-copy">📋 Copy</button>
    </div>
    <div class="collapsible-content">
        <pre><code>Your code here...</code></pre>
    </div>
</div>
JavaScript
// Toggle collapsible on header click
document.querySelectorAll('.collapsible-header').forEach(header => {
    header.addEventListener('click', function(e) {
        // Don't toggle if clicking the copy button
        if (e.target.closest('.collapsible-copy')) return;
        
        const collapsible = this.closest('.collapsible');
        collapsible.classList.toggle('open');
    });
});

// Copy button functionality
document.querySelectorAll('.collapsible-copy').forEach(btn => {
    btn.addEventListener('click', function(e) {
        e.stopPropagation();
        const collapsible = this.closest('.collapsible');
        const codeEl = collapsible.querySelector('code');
        const text = codeEl.textContent;
        
        navigator.clipboard.writeText(text).then(() => {
            const originalText = this.innerHTML;
            this.innerHTML = '✓ Copied';
            this.classList.add('copied');
            setTimeout(() => {
                this.innerHTML = originalText;
                this.classList.remove('copied');
            }, 2000);
        });
    });
});

Tables

Data tables with header styling, row hover, and proper spacing. Last row omits border for symmetric padding.

Live Example
Name Type Cost
Server 01 Rack $500
Server 02 Blade $750
CSS
.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    line-height: 1.3;
    margin: 0;
}

.table th {
    text-align: left;
    color: #58a6ff;
    font-weight: 600;
    padding: 0 12px 10px 12px;
    border-bottom: 1px solid #30363d;
    font-size: 11px;
    text-transform: uppercase;
}

.table td {
    padding: 10px 12px;
    border-bottom: 1px solid rgba(48, 54, 61, 0.5);
    vertical-align: top;
}

/* Last row: no border, no bottom padding */
.table tr:last-child td {
    border-bottom: none;
    padding-bottom: 0;
}

.table tr:hover td {
    background: rgba(88, 166, 255, 0.05);
}
HTML
<table class="table">
    <thead>
        <tr>
            <th>Name</th>
            <th>Type</th>
            <th>Cost</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Server 01</td>
            <td>Rack</td>
            <td>$500</td>
        </tr>
    </tbody>
</table>

Callout / Alert Box

Alert boxes in three variants: warning (orange), success (green), and error (red).

Live Example
⚠️ Warning
This action cannot be undone.
✓ Success
Data saved successfully.
✕ Error
Connection failed.
CSS
/* Warning/Info Callout (default) */
.callout {
    background: rgba(240, 136, 62, 0.08);
    border: 1px solid #f0883e;
    border-radius: 6px;
    padding: 16px;
}

/* Success Callout */
.callout.success {
    background: rgba(0, 255, 136, 0.08);
    border-color: #00ff88;
}

/* Error Callout */
.callout.error {
    background: rgba(248, 81, 73, 0.08);
    border-color: #f85149;
}
HTML
<!-- Warning (default) -->
<div class="callout">
    <strong>⚠️ Warning</strong>
    <p>This action cannot be undone.</p>
</div>

<!-- Success -->
<div class="callout success">...</div>

<!-- Error -->
<div class="callout error">...</div>

Tooltip

Floating feedback tooltip for copy actions and hover hints. Fixed position with high z-index.

Live Example
Copied!
CSS
.tooltip {
    position: fixed;
    background: #238636;
    color: #fff;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    z-index: 1000;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
}

.tooltip.show {
    opacity: 1;
}
HTML
<div class="tooltip" id="tooltip">Copied!</div>
JavaScript
function showTooltip(x, y, text) {
    const tooltip = document.getElementById('tooltip');
    tooltip.textContent = text;
    tooltip.style.left = x + 'px';
    tooltip.style.top = y + 'px';
    tooltip.classList.add('show');
    setTimeout(() => {
        tooltip.classList.remove('show');
    }, 1500);
}

Progress / Stats Bar

Resource usage bars with three color states: normal (green), warning (orange), and danger (red).

Live Example
CPU
7/16
Memory
12/16
Storage
92%
CSS
.stat-bar {
    display: flex;
    align-items: center;
    gap: 10px;
}

.stat-track {
    flex: 1;
    height: 8px;
    background: rgba(22, 27, 34, 0.8);
    border-radius: 4px;
    overflow: hidden;
}

.stat-fill {
    height: 100%;
    background: #00ff88;
    transition: width 0.3s;
}

.stat-fill.warning { background: #f0883e; }
.stat-fill.danger { background: #f85149; }
HTML
<div class="stat-bar">
    <span class="stat-label">CPU</span>
    <div class="stat-track">
        <div class="stat-fill" style="width: 45%"></div>
    </div>
    <span class="stat-value">7/16</span>
</div>

<!-- Warning state (orange) -->
<div class="stat-fill warning" style="width: 75%"></div>

<!-- Danger state (red) -->
<div class="stat-fill danger" style="width: 92%"></div>

Empty State

Placeholder message for empty containers with optional hint text.

Live Example
No programs selected Click programs above to add them
CSS
.empty-state {
    text-align: center;
    color: #8b949e;
    padding: 40px;
    font-size: 12px;
}

.empty-state small {
    display: block;
    margin-top: 8px;
    color: #7d8590;
}
HTML
<div class="empty-state">
    No programs selected
    <small>Click programs above to add them</small>
</div>

Copy Feedback

Click-to-copy elements with visual hover and copied states.

Live Example
SEED-12345 ← hover state
SEED-12345 ✓ copied state
CSS
/* ─────────────────────────────────────────────────────────
   DEPENDENCY: Tooltip (see UI Components › Tooltip)
   Include once per page. Skip if already defined elsewhere.
   ───────────────────────────────────────────────────────── */
.tooltip {
    position: fixed;
    background: #238636;
    color: #fff;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 1000;
}
.tooltip.show { opacity: 1; }

/* ─────────────────────────────────────────────────────────
   Copyable Element
   ───────────────────────────────────────────────────────── */
.copyable {
    cursor: pointer;
    transition: background 0.15s;
}

.copyable:hover {
    background: rgba(0, 255, 136, 0.15);
}

.copyable.copied {
    background: rgba(35, 134, 54, 0.3);
}
HTML
<!-- DEPENDENCY: Tooltip (see UI Components › Tooltip)
     Include once per page. Skip if already defined elsewhere. -->
<div class="tooltip" id="tooltip">Copied!</div>

<!-- Copyable element -->
<code class="copyable" onclick="copyValue(this)">SEED-12345</code>
JavaScript
// Requires: Tooltip HTML element with id="tooltip"
function copyValue(el) {
    navigator.clipboard.writeText(el.textContent.trim()).then(() => {
        el.classList.add('copied');
        const tooltip = document.getElementById('tooltip');
        const rect = el.getBoundingClientRect();
        tooltip.style.left = rect.left + rect.width/2 - 40 + 'px';
        tooltip.style.top = rect.top - 40 + 'px';
        tooltip.classList.add('show');
        setTimeout(() => {
            el.classList.remove('copied');
            tooltip.classList.remove('show');
        }, 1500);
    });
}
📝

Patterns & Examples

Selectable Item Grid

Interactive card grid for selecting items. Click to toggle selection state. Uses the standard card component with selection behavior.

Live Example
Click cards to toggle selection
DNS-Lite
💰 200
Firewall
💰 350
VPN
💰 500
HTML
<div class="grid">
    <div class="card" onclick="this.classList.toggle('selected')">
        <div class="card-name">Item Name</div>
        <div class="card-meta">💰 500</div>
    </div>
</div>
JavaScript
// Toggle selection on click
document.querySelectorAll('.card:not(.disabled)').forEach(card => {
    card.addEventListener('click', () => {
        card.classList.toggle('selected');
    });
});

Copyable Code/Value

Display prominent values like seeds or codes that users can click to copy. Uses green accent with subtle glow.

Live Example
Click to copy seed value
SEED-7X9K2
CSS
/* ─────────────────────────────────────────────────────────
   DEPENDENCY: Tooltip (see UI Components › Tooltip)
   Include once per page. Skip if already defined elsewhere.
   ───────────────────────────────────────────────────────── */
.tooltip {
    position: fixed;
    background: #238636;
    color: #fff;
    padding: 6px 12px;
    border-radius: 4px;
    font-weight: 500;
    font-size: 11px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 1000;
}
.tooltip.show { opacity: 1; }

/* ─────────────────────────────────────────────────────────
   Copyable Code Value
   ───────────────────────────────────────────────────────── */
.code-value {
    font-size: 1.4em;
    font-weight: 600;
    color: #00ff88;
    text-align: center;
    padding: 10px 20px;
    background: rgba(0, 255, 136, 0.08);
    border: 1px solid #238636;
    border-radius: 4px;
    letter-spacing: 4px;
    cursor: pointer;
    transition: background 0.15s;
}

.code-value:hover {
    background: rgba(0, 255, 136, 0.15);
}

.code-value.copied {
    background: rgba(35, 134, 54, 0.3);
}
HTML
<!-- DEPENDENCY: Tooltip (see UI Components › Tooltip)
     Include once per page. Skip if already defined elsewhere. -->
<div class="tooltip" id="tooltip">Copied!</div>

<!-- Copyable Code Value -->
<div class="code-value" onclick="copyValue(this)">SEED-7X9K2</div>
JavaScript
// Requires: Tooltip HTML element with id="tooltip"
function copyValue(el) {
    navigator.clipboard.writeText(el.textContent.trim()).then(() => {
        el.classList.add('copied');
        const tooltip = document.getElementById('tooltip');
        const rect = el.getBoundingClientRect();
        tooltip.style.left = rect.left + rect.width/2 - 40 + 'px';
        tooltip.style.top = rect.top - 40 + 'px';
        tooltip.classList.add('show');
        setTimeout(() => {
            el.classList.remove('copied');
            tooltip.classList.remove('show');
        }, 1500);
    });
}

Inline Code

Style for code snippets within text. Uses green accent with dark background for visibility.

Live Example

Edit tni-store.json to add items

Run program install dns-lite on 123

CSS
code {
    background: rgba(0, 0, 0, 0.3);
    padding: 2px 6px;
    border-radius: 3px;
    font-size: 11px;
    color: #00ff88;
}
HTML
<p>Edit <code>tni-store.json</code> to add items</p>

Filter/Search Box

Search input for filtering lists or grids. Uses the standard form input styling with search icon placeholder.

Live Example
HTML
<input 
    type="text" 
    id="search"
    class="input"
    placeholder="🔍 Filter programs..."
    oninput="filterItems(this.value)"
>
JavaScript
function filterItems(query) {
    const lower = query.toLowerCase();
    document.querySelectorAll('.card').forEach(card => {
        const name = card.querySelector('.card-name').textContent.toLowerCase();
        card.style.display = name.includes(lower) ? 'block' : 'none';
    });
}
📱

Responsive Design

Breakpoints

/* Mobile: 600px and below */
@media (max-width: 600px) {
    body { padding: 16px; }
    
    .container { padding: 16px; }
    
    h1 { font-size: 18px; }
    
    .grid { grid-template-columns: 1fr; }
    
    .flex-row {
        flex-direction: column;
        align-items: stretch;
    }
}

Container Max-Widths

  • Default tools: max-width: 1200px
  • Narrower/reading layouts: max-width: 1100px
  • Compact tools: max-width: 900px

Best Practices

✓ DO

  • Use the established color tokens
  • Keep font sizes consistent with the scale
  • Use transition: all 0.15s for hover effects
  • Include the standard footer on all tools
  • Test offline functionality
  • Embed all data directly in the HTML
  • Use semantic emoji in headers (📋 📊 🎯 etc.)

✗ DON'T

  • Import external CSS or JS files
  • Use colors outside the palette
  • Use fonts other than the monospace stack
  • Create separate CSS/JS files
  • Rely on network requests for data
  • Use heavy animations or effects
  • Forget the mobile breakpoint

Accessibility

  • Maintain sufficient color contrast (green on dark passes)
  • Use cursor: pointer on clickable elements
  • Include title attributes on interactive elements
  • Keep font sizes readable (minimum 10px)

Emoji Reference

EmojiUsage
📋Selection, configuration
📊Data, statistics, results
🎯Results, matches, targets
🛠️Tools, settings
🗺️Roadmap, planning
💰Cost, currency
Power, energy
📜Policy, rules
🦄Unique/rare items
Complete, done
Pending, incomplete
📄

File Format Standards

All project files include standardized headers for version tracking, attribution, and changelog. This ensures consistency across all file types and makes contributions traceable.

JSON Files

JSON data files include a _meta object as the first key:

{
  "_meta": {
    "game": "Tower Networking Inc.",
    "dataset": "dataset-name",
    "version": "1.0.0",
    "last_updated": "YYYY-MM-DD",
    "description": "What this dataset contains",
    "sources": [...],
    "contributors": ["Name or Handle"],
    "corrections": [...],
    "future_additions": [...]
  }
}
FieldRequiredDescription
gameYesAlways "Tower Networking Inc."
datasetYesIdentifier for the dataset (e.g., "programs", "servers")
versionYesSemantic version (X.Y.Z)
last_updatedYesLast modified date (YYYY-MM-DD)
descriptionYesWhat this dataset contains
sourcesNoArray of data sources (in-game, wiki, etc.)
contributorsYesArray of contributor names/handles
correctionsNoArray of data corrections made
future_additionsNoArray of planned additions

HTML Files

HTML tools include a comment header before <!DOCTYPE html>:

<!--
╔══════════════════════════════════════════════════════════════════════════════╗
║  Tool Name                                                                   ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version: X.Y.Z                                                              ║
║  Updated: YYYY-MM-DD                                                         ║
║  Part of: TNI Toolkit (https://github.com/salvo-praxis/tni-toolkit)          ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Description:                                                                ║
║    Brief description of what this tool does.                                 ║
║                                                                              ║
║  Contributors:                                                               ║
║    - Name (contribution type)                                                ║
║                                                                              ║
║  Changelog:                                                                  ║
║    X.Y.Z - Description of changes                                            ║
╚══════════════════════════════════════════════════════════════════════════════╝
-->
FieldRequiredDescription
Tool NameYesName displayed in the header box
VersionYesSemantic version (X.Y.Z)
UpdatedYesLast modified date (YYYY-MM-DD)
Part ofYesAlways "TNI Toolkit" with GitHub URL
DescriptionYesWhat the tool does (can be multi-line)
ContributorsYesList with contribution types in parentheses
ChangelogYesVersion history with descriptions

YAML Files

YAML configuration files (GitHub Actions, CI/CD) use comment headers:

# ============================================================================
# TNI Toolkit - [File Type/Purpose]
# File: [path/to/file.yml]
# Name: [Workflow or Config Name]
# Version: X.Y.Z
# Updated: YYYY-MM-DD
# Part of: TNI Toolkit (https://github.com/salvo-praxis/tni-toolkit)
# Description:
#   Brief description of what this file does.
#   Can span multiple lines if needed.
# Contributors:
#   - Name (@handle) - contribution type
#   - Claude - contribution type
# Changelog:
#   X.Y.Z - Description of changes
# ============================================================================
FieldRequiredDescription
FileYesPath relative to repo root
NameYesHuman-readable name (matches workflow name: key)
VersionYesSemantic version (X.Y.Z)
UpdatedYesLast modified date (YYYY-MM-DD)
Part ofYesAlways "TNI Toolkit" with GitHub URL
DescriptionYesWhat the file does (can be multi-line)
ContributorsYesList of contributors with @handles and roles
ChangelogYesVersion history with descriptions

Python Files

Python scripts use docstring headers:

#!/usr/bin/env python3
"""
TNI Toolkit - [Script Name]
File: [path/to/script.py]
Version: X.Y.Z
Updated: YYYY-MM-DD
Part of: TNI Toolkit (https://github.com/salvo-praxis/tni-toolkit)

Description:
    Brief description of what this script does.
    Can span multiple lines if needed.

Contributors:
    - Name (@handle) - contribution type
    - Claude - contribution type

Changelog:
    X.Y.Z - Description of changes
"""
FieldRequiredDescription
ShebangYesAlways #!/usr/bin/env python3
Script NameYesHuman-readable name of the script
FileYesPath relative to repo root
VersionYesSemantic version (X.Y.Z)
UpdatedYesLast modified date (YYYY-MM-DD)
Part ofYesAlways "TNI Toolkit" with GitHub URL
DescriptionYesWhat the script does (indented, can be multi-line)
ContributorsYesList of contributors with @handles and roles
ChangelogYesVersion history with descriptions

Shell Scripts

Bash

#!/bin/bash
# ============================================================================
# TNI Toolkit - [Script Name]
# File: [path/to/script.sh]
# Version: X.Y.Z
# Updated: YYYY-MM-DD
# Part of: TNI Toolkit (https://github.com/salvo-praxis/tni-toolkit)
# Description:
#   Brief description of what this script does.
# Contributors:
#   - Name (@handle) - contribution type
# Changelog:
#   X.Y.Z - Description of changes
# ============================================================================

PowerShell

<#
============================================================================
TNI Toolkit - [Script Name]
File: [path/to/script.ps1]
Version: X.Y.Z
Updated: YYYY-MM-DD
Part of: TNI Toolkit (https://github.com/salvo-praxis/tni-toolkit)
Description:
    Brief description of what this script does.
Contributors:
    - Name (@handle) - contribution type
Changelog:
    X.Y.Z - Description of changes
============================================================================
#>
FieldRequiredDescription
ShebangYes#!/bin/bash for Bash; none for PowerShell
Script NameYesHuman-readable name of the script
FileYesPath relative to repo root
VersionYesSemantic version (X.Y.Z)
UpdatedYesLast modified date (YYYY-MM-DD)
Part ofYesAlways "TNI Toolkit" with GitHub URL
DescriptionYesWhat the script does
ContributorsYesList of contributors with @handles and roles
ChangelogYesVersion history with descriptions
Copied!