-
{title}
- {description &&
{description}
}
+
+
+
{title}
+ {description &&
{description}
}
{children}
diff --git a/crowdsec-docs/src/css/alerts.css b/crowdsec-docs/src/css/alerts.css
index 020265122..33aad7961 100644
--- a/crowdsec-docs/src/css/alerts.css
+++ b/crowdsec-docs/src/css/alerts.css
@@ -12,8 +12,12 @@
top: 0.2rem !important;
}
+.alert .cs-admon__icon {
+ @apply p-0.5;
+}
+
.alert svg {
- @apply !w-5 !h-5 mb-0;
+ @apply !w-4 !h-4 mb-0;
}
.alert--info {
diff --git a/crowdsec-docs/src/css/crowdsec-algolia.css b/crowdsec-docs/src/css/crowdsec-algolia.css
new file mode 100644
index 000000000..4a4bcac42
--- /dev/null
+++ b/crowdsec-docs/src/css/crowdsec-algolia.css
@@ -0,0 +1,60 @@
+/* ─────────────────────────────────────────────────────────────────────────────
+ CrowdSec Algolia DocSearch theming
+ Maps --docsearch-* vars to --cs-* tokens for dark and light modes.
+───────────────────────────────────────────────────────────────────────────── */
+
+[data-theme="dark"] .DocSearch,
+:root .DocSearch {
+ --docsearch-primary-color: var(--cs-orange);
+ --docsearch-text-color: var(--cs-ink);
+ --docsearch-muted-color: var(--cs-ink-mute);
+ --docsearch-container-background: rgba(10, 17, 32, 0.8);
+ --docsearch-modal-background: var(--cs-surface);
+ --docsearch-searchbox-background: var(--cs-bg);
+ --docsearch-searchbox-focus-background: var(--cs-bg-soft);
+ --docsearch-hit-color: var(--cs-ink);
+ --docsearch-hit-active-color: var(--cs-bg);
+ --docsearch-hit-background: var(--cs-surface-2);
+ --docsearch-footer-background: var(--cs-bg-soft);
+ --docsearch-key-gradient: linear-gradient(180deg, var(--cs-surface) 0%, var(--cs-bg) 100%);
+ --docsearch-key-shadow: 0 1px 0 var(--cs-border-hi), 0 1px 3px rgba(0, 0, 0, 0.35);
+ --docsearch-searchbox-shadow: inset 0 0 0 1px var(--cs-orange);
+ --docsearch-modal-shadow: 0 8px 40px rgba(0, 0, 0, 0.4);
+}
+
+[data-theme="light"] .DocSearch {
+ --docsearch-primary-color: var(--cs-orange);
+ --docsearch-text-color: var(--cs-ink);
+ --docsearch-muted-color: var(--cs-ink-mute);
+ --docsearch-container-background: rgba(15, 23, 42, 0.55);
+ --docsearch-modal-background: var(--cs-surface);
+ --docsearch-searchbox-background: var(--cs-surface-2);
+ --docsearch-searchbox-focus-background: var(--cs-bg-soft);
+ --docsearch-hit-color: var(--cs-ink);
+ --docsearch-hit-active-color: #ffffff;
+ --docsearch-hit-background: var(--cs-bg-soft);
+ --docsearch-footer-background: var(--cs-surface-2);
+ --docsearch-key-gradient: linear-gradient(180deg, var(--cs-bg-soft) 0%, var(--cs-surface-2) 100%);
+ --docsearch-key-shadow: 0 1px 0 var(--cs-border-hi), 0 1px 3px rgba(0, 0, 0, 0.15);
+ --docsearch-searchbox-shadow: inset 0 0 0 1px var(--cs-orange);
+ --docsearch-modal-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
+}
+
+/* Homepage search bar override — use token vars */
+.homepage-search .DocSearch-Button {
+ background: var(--cs-surface) !important;
+ border-color: var(--cs-border-hi) !important;
+}
+
+.homepage-search .DocSearch-Button:hover {
+ border-color: var(--cs-orange) !important;
+ box-shadow: 0 0 0 1px var(--cs-orange) !important;
+}
+
+.homepage-search .DocSearch-Button-Placeholder {
+ color: var(--cs-ink-mute) !important;
+}
+
+.homepage-search .DocSearch-Search-Icon {
+ color: var(--cs-ink-mute) !important;
+}
diff --git a/crowdsec-docs/src/css/crowdsec-components.css b/crowdsec-docs/src/css/crowdsec-components.css
new file mode 100644
index 000000000..c0b64443e
--- /dev/null
+++ b/crowdsec-docs/src/css/crowdsec-components.css
@@ -0,0 +1,706 @@
+/* ─────────────────────────────────────────────────────────────────────────────
+ CrowdSec Component Styles
+ Inline code · Admonitions · Code blocks · Tables
+ All colours via --cs-* tokens — no hex in this file.
+───────────────────────────────────────────────────────────────────────────── */
+
+/* Extra tokens needed here */
+:root,
+[data-theme="dark"] {
+ --cs-red: #f87171;
+ --syn-keyword: var(--cs-pink);
+ --syn-string: #fbbf24;
+ --syn-fn: var(--cs-blue);
+ --syn-number: var(--cs-violet);
+ --syn-comment: var(--cs-ink-mute);
+ --syn-punct: var(--cs-ink-dim);
+ --syn-text: #e2e8f0;
+ --syn-flag: var(--cs-pink);
+}
+
+[data-theme="light"] {
+ --cs-red: #dc2626;
+ --syn-keyword: var(--cs-pink);
+ --syn-string: #b45309;
+ --syn-fn: var(--cs-blue);
+ --syn-number: var(--cs-violet);
+ --syn-comment: #94a3b8;
+ --syn-punct: var(--cs-ink-dim);
+ --syn-text: #1e293b;
+ --syn-flag: var(--cs-pink);
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 1. INLINE CODE
+ ═══════════════════════════════════════════════════════════════════ */
+code:not(pre code):not(.cs-ic) {
+ font-family: var(--cs-font-mono) !important;
+ font-size: 0.88em;
+ padding: 1px 6px 2px !important;
+ border-radius: 5px !important;
+ background: color-mix(in srgb, rgb(var(--primary)) 10%, transparent) !important;
+ color: rgb(var(--primary)) !important;
+ border: 1px solid color-mix(in srgb, rgb(var(--primary)) 22%, transparent) !important;
+ white-space: nowrap;
+ font-weight: 500 !important;
+}
+
+/* Semantic variants — applied via JSX className or remark plugin */
+.cs-ic {
+ font-family: var(--cs-font-mono);
+ font-size: 0.88em;
+ padding: 1px 6px 2px;
+ border-radius: 5px;
+ white-space: nowrap;
+ font-weight: 500;
+ background: color-mix(in srgb, rgb(var(--primary)) 10%, transparent);
+ color: rgb(var(--primary));
+ border: 1px solid color-mix(in srgb, rgb(var(--primary)) 22%, transparent);
+}
+.cs-ic--path {
+ background: color-mix(in srgb, var(--cs-blue) 12%, transparent);
+ color: var(--cs-blue);
+ border-color: color-mix(in srgb, var(--cs-blue) 22%, transparent);
+}
+.cs-ic--kw {
+ background: color-mix(in srgb, var(--cs-violet) 12%, transparent);
+ color: var(--cs-violet);
+ border-color: color-mix(in srgb, var(--cs-violet) 22%, transparent);
+}
+.cs-ic--val {
+ background: color-mix(in srgb, var(--cs-teal) 12%, transparent);
+ color: var(--cs-teal);
+ border-color: color-mix(in srgb, var(--cs-teal) 22%, transparent);
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 2. ADMONITIONS
+ ═══════════════════════════════════════════════════════════════════ */
+.cs-admon {
+ --tone: var(--cs-blue);
+ margin: 18px 0;
+ padding: 14px 16px 14px 14px;
+ border-radius: 10px;
+ background:
+ linear-gradient(0deg, color-mix(in srgb, var(--tone) 5%, transparent), color-mix(in srgb, var(--tone) 5%, transparent)),
+ var(--cs-surface);
+ border: 1px solid color-mix(in srgb, var(--tone) 24%, var(--cs-border)) !important;
+ border-left: 4px solid var(--tone) !important;
+ display: grid;
+ grid-template-columns: 22px 1fr;
+ gap: 12px;
+ align-items: start;
+}
+
+.cs-admon__icon {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: color-mix(in srgb, var(--tone) 18%, transparent);
+ color: var(--tone);
+ flex-shrink: 0;
+ margin-top: 2px;
+}
+
+.cs-admon__icon svg {
+ width: 13px;
+ height: 13px;
+}
+
+.cs-admon__body {
+ min-width: 0;
+}
+
+.cs-admon__title {
+ font-family: var(--cs-font-mono);
+ font-size: 10.5px;
+ font-weight: 700;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--tone);
+ margin-bottom: 4px;
+}
+
+.cs-admon__content {
+ font-size: 14.5px;
+ line-height: 1.55;
+ color: var(--cs-ink);
+}
+
+.cs-admon__content > *:last-child {
+ margin-bottom: 0;
+}
+.cs-admon__content a,
+.cs-admon__content a:hover {
+ color: var(--tone) !important;
+ font-weight: 600;
+ text-decoration: underline;
+ text-underline-offset: 3px;
+ text-decoration-thickness: 1.5px;
+ opacity: 1 !important;
+}
+
+/* Type tones */
+.cs-admon.note {
+ --tone: var(--cs-blue);
+}
+.cs-admon.tip {
+ --tone: var(--cs-teal);
+}
+.cs-admon.info {
+ --tone: var(--cs-blue);
+}
+.cs-admon.warning {
+ --tone: var(--cs-orange);
+}
+.cs-admon.danger {
+ --tone: var(--cs-red);
+}
+.cs-admon.premium {
+ --tone: var(--cs-violet);
+}
+.cs-admon.caution {
+ --tone: var(--cs-orange);
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 3. CODE BLOCKS
+ ═══════════════════════════════════════════════════════════════════ */
+
+/* Zero Infima's pre-padding token so the .codeBlock_hash module class
+ contributes 0 padding; only our chrome and .prism-code set sizing */
+:root,
+[data-theme="dark"],
+[data-theme="light"] {
+ --ifm-pre-padding: 0;
+}
+
+/* Outer container */
+.theme-code-block,
+[class*="codeBlockContainer"] {
+ border: 1px solid var(--cs-border) !important;
+ border-radius: 10px !important;
+ overflow: hidden !important;
+ background: var(--cs-surface) !important;
+ box-shadow: none !important;
+ margin: 18px 0 !important;
+}
+
+/* Hide the default Docusaurus title bar — chrome swizzle handles it */
+[class*="codeBlockTitle"] {
+ display: none !important;
+}
+
+/* Hide the default button group — chrome swizzle handles copy */
+[class*="buttonGroup"],
+[class*="codeBlockButtons"] {
+ display: none !important;
+}
+
+/* ── Chrome bar (injected by CodeBlock/Content swizzle) ─────────── */
+.cs-codeblock-outer {
+ display: flex;
+ flex-direction: column;
+}
+
+.cs-codeblock-chrome {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 10px 8px 14px;
+ border-bottom: 1px solid var(--cs-border);
+ background: var(--cs-bg-soft);
+ min-height: 38px;
+}
+
+.cs-codeblock-lang {
+ font-family: var(--cs-font-mono);
+ font-size: 10.5px;
+ font-weight: 600;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--cs-ink-dim);
+ flex-shrink: 0;
+}
+
+.cs-codeblock-title {
+ font-family: var(--cs-font-mono);
+ font-size: 12px;
+ color: var(--cs-ink-dim);
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Position COPY button at right in chrome */
+.cs-codeblock-chrome .cs-codeblock__copy {
+ margin-left: auto;
+}
+
+/* ── Code area ─────────────────────────────────────────────────── */
+/* Zero any wrapper padding — only .prism-code sets it */
+[class*="codeBlockContent"],
+[class*="codeBlockScrollbarWrapper"],
+[class*="codeBlockLines"],
+.cs-codeblock-body {
+ padding: 0 !important;
+}
+
+/* Pre / code area — single source of padding */
+.prism-code {
+ background: var(--cs-surface) !important;
+ border: none !important;
+ border-radius: 0 !important;
+ margin: 0 !important;
+ padding: 14px 16px !important;
+ font-family: var(--cs-font-mono) !important;
+ font-size: 13px !important;
+ line-height: 1.65 !important;
+ color: var(--syn-text) !important;
+}
+
+/* Line numbers column */
+.prism-code .token-line .linenumber,
+.prism-code span[class*="lineNumber"] {
+ display: inline-block !important;
+ min-width: 2.2em !important;
+ color: var(--cs-ink-mute) !important;
+ user-select: none;
+ text-align: right;
+ padding-right: 14px !important;
+ border-right: 1px solid var(--cs-border) !important;
+ margin-right: 14px !important;
+ font-size: 12.5px !important;
+}
+
+/* Line highlighting */
+.docusaurus-highlight-code-line,
+.theme-code-block-highlighted-line {
+ background: color-mix(in srgb, var(--cs-orange) 10%, transparent) !important;
+ border-left: 2px solid var(--cs-orange) !important;
+ margin-left: -16px !important;
+ padding-left: 14px !important;
+ display: block;
+}
+
+/* ── Prism token overrides ─────────────────────────────────────── */
+.token.keyword,
+.token.operator,
+.token.selector {
+ color: var(--syn-keyword) !important;
+}
+
+.token.string,
+.token.char,
+.token.url,
+.token.regex {
+ color: var(--syn-string) !important;
+}
+
+.token.function,
+.token.method {
+ color: var(--syn-fn) !important;
+}
+
+.token.number,
+.token.boolean {
+ color: var(--syn-number) !important;
+}
+
+.token.comment,
+.token.block-comment,
+.token.prolog,
+.token.doctype {
+ color: var(--syn-comment) !important;
+ font-style: italic;
+}
+
+.token.tag,
+.token.attr-name,
+.token.namespace {
+ color: var(--cs-blue) !important;
+}
+
+.token.punctuation,
+.token.delimiter {
+ color: var(--syn-punct) !important;
+}
+
+.token.property,
+.token.variable {
+ color: var(--cs-orange) !important;
+}
+
+.token.important {
+ color: var(--syn-flag) !important;
+}
+
+/* Diff support */
+.token-line.inserted,
+[class*="codeLine"].inserted,
+.diff.added {
+ background: color-mix(in srgb, var(--cs-teal) 8%, transparent) !important;
+}
+
+.token-line.deleted,
+[class*="codeLine"].deleted,
+.diff.removed {
+ background: color-mix(in srgb, var(--cs-red) 8%, transparent) !important;
+ text-decoration: line-through;
+}
+
+/* Copy button — styled by swizzle, but add fallback via CSS */
+[class*="copyButton"],
+[class*="buttonGroup"] button {
+ background: transparent !important;
+ border: 1px solid var(--cs-border-hi) !important;
+ color: var(--cs-ink-dim) !important;
+ border-radius: 5px !important;
+ transition:
+ color 0.15s,
+ border-color 0.15s,
+ background 0.15s !important;
+}
+
+[class*="copyButton"]:hover,
+[class*="buttonGroup"] button:hover {
+ color: var(--cs-orange) !important;
+ border-color: color-mix(in srgb, var(--cs-orange) 40%, transparent) !important;
+ background: color-mix(in srgb, var(--cs-orange) 8%, transparent) !important;
+}
+
+/* Custom copy button class */
+.cs-codeblock__copy {
+ background: transparent;
+ border: 1px solid var(--cs-border-hi);
+ color: var(--cs-ink-dim);
+ font-family: var(--cs-font-mono);
+ font-size: 10.5px;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ padding: 4px 9px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition:
+ color 0.15s,
+ border-color 0.15s,
+ background 0.15s;
+}
+
+.cs-codeblock__copy:hover {
+ color: var(--cs-orange);
+ border-color: color-mix(in srgb, var(--cs-orange) 40%, transparent);
+ background: color-mix(in srgb, var(--cs-orange) 8%, transparent);
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 4. TABLES
+ CSS-only approach — works on both Markdown and HTML tables.
+ Uses border-collapse:separate + corner-radius on cells for rounded
+ table appearance without needing a wrapper div.
+ ═══════════════════════════════════════════════════════════════════ */
+
+/* Wrapper added by MDXComponents table override (Markdown tables) */
+.cs-table-wrap {
+ margin: 18px 0;
+ border: 1px solid var(--cs-border);
+ border-radius: 10px;
+ overflow: hidden;
+ background: var(--cs-surface);
+}
+
+/* ── Direct table targeting (catches HTML tables too) ─────────── */
+
+/* Infima sets display:block on tables inside .markdown — undo it so width works */
+.theme-doc-markdown table,
+article table,
+.markdown table {
+ display: table !important;
+ width: 100% !important;
+ min-width: 100% !important;
+ border-collapse: separate !important;
+ border-spacing: 0 !important;
+ font-size: 14px !important;
+ background: var(--cs-surface) !important;
+ border: 1px solid var(--cs-border) !important;
+ border-radius: 10px !important;
+ margin: 18px 0 !important;
+ overflow: hidden !important;
+ table-layout: auto !important;
+}
+
+/* thead and tbody must also be display:table-header-group etc. for width to propagate */
+.theme-doc-markdown table thead,
+article table thead,
+.markdown table thead {
+ display: table-header-group !important;
+ width: 100% !important;
+}
+
+.theme-doc-markdown table tbody,
+article table tbody,
+.markdown table tbody {
+ display: table-row-group !important;
+ width: 100% !important;
+}
+
+.theme-doc-markdown table tr,
+article table tr,
+.markdown table tr {
+ display: table-row !important;
+ width: 100% !important;
+}
+
+.theme-doc-markdown table thead th,
+article table thead th {
+ text-align: left !important;
+ padding: 10px 16px !important;
+ font-family: var(--cs-font-mono) !important;
+ font-size: 10.5px !important;
+ font-weight: 700 !important;
+ letter-spacing: 0.14em !important;
+ text-transform: uppercase !important;
+ color: var(--cs-ink-dim) !important;
+ background: var(--cs-bg-soft) !important;
+ border-bottom: 1px solid var(--cs-border) !important;
+ /* rounded top corners via first/last th */
+}
+
+.theme-doc-markdown table thead tr:first-child th:first-child,
+article table thead tr:first-child th:first-child {
+ border-radius: 10px 0 0 0 !important;
+}
+
+.theme-doc-markdown table thead tr:first-child th:last-child,
+article table thead tr:first-child th:last-child {
+ border-radius: 0 10px 0 0 !important;
+}
+
+.theme-doc-markdown table tbody td,
+article table tbody td {
+ padding: 12px 16px !important;
+ border-bottom: 1px solid var(--cs-border) !important;
+ color: var(--cs-ink) !important;
+ line-height: 1.5 !important;
+ vertical-align: top !important;
+ background: var(--cs-surface) !important;
+}
+
+.theme-doc-markdown table tbody tr:last-child td,
+article table tbody tr:last-child td {
+ border-bottom: none !important;
+}
+
+.theme-doc-markdown table tbody tr:last-child td:first-child,
+article table tbody tr:last-child td:first-child {
+ border-radius: 0 0 0 10px !important;
+}
+
+.theme-doc-markdown table tbody tr:last-child td:last-child,
+article table tbody tr:last-child td:last-child {
+ border-radius: 0 0 10px 0 !important;
+}
+
+.theme-doc-markdown table tbody tr:hover td,
+article table tbody tr:hover td {
+ background: color-mix(in srgb, var(--cs-orange) 4%, transparent) !important;
+}
+
+/* ── Reset table styles when inside cs-table-wrap (after all general rules so this wins) */
+.cs-table-wrap table {
+ margin: 0 !important;
+ border: none !important;
+ border-radius: 0 !important;
+ overflow: visible !important;
+}
+
+/* ── Pill component ─────────────────────────────────────────────── */
+.cs-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 2px 8px;
+ border-radius: 999px;
+ font-family: var(--cs-font-mono);
+ font-size: 10.5px;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ background: color-mix(in srgb, var(--cs-violet) 14%, transparent);
+ color: var(--cs-violet);
+ border: 1px solid color-mix(in srgb, var(--cs-violet) 28%, transparent);
+ vertical-align: middle;
+ white-space: nowrap;
+}
+
+.cs-pill.community,
+.cs-pill[data-variant="community"] {
+ background: color-mix(in srgb, var(--cs-teal) 14%, transparent);
+ color: var(--cs-teal);
+ border-color: color-mix(in srgb, var(--cs-teal) 28%, transparent);
+}
+
+.cs-pill.platinum,
+.cs-pill[data-variant="platinum"] {
+ background: color-mix(in srgb, var(--cs-orange) 14%, transparent);
+ color: var(--cs-orange);
+ border-color: color-mix(in srgb, var(--cs-orange) 28%, transparent);
+}
+
+.cs-pill.info,
+.cs-pill[data-variant="info"] {
+ background: color-mix(in srgb, var(--cs-blue) 14%, transparent);
+ color: var(--cs-blue);
+ border-color: color-mix(in srgb, var(--cs-blue) 28%, transparent);
+}
+
+.cs-pill.success,
+.cs-pill[data-variant="success"] {
+ background: color-mix(in srgb, var(--cs-teal) 14%, transparent);
+ color: var(--cs-teal);
+ border-color: color-mix(in srgb, var(--cs-teal) 28%, transparent);
+}
+
+.cs-pill.warning,
+.cs-pill[data-variant="warning"] {
+ background: color-mix(in srgb, var(--cs-orange) 14%, transparent);
+ color: var(--cs-orange);
+ border-color: color-mix(in srgb, var(--cs-orange) 28%, transparent);
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 5. TABS
+ ═══════════════════════════════════════════════════════════════════ */
+
+/* Tab bar */
+.tabs {
+ border-bottom: 1px solid var(--cs-border) !important;
+ margin-bottom: 0 !important;
+ gap: 8px !important;
+ flex-wrap: nowrap !important;
+ overflow-x: auto !important;
+ scrollbar-width: none !important;
+}
+
+.tabs::-webkit-scrollbar {
+ display: none !important;
+}
+
+/* Individual tab items */
+.tabs__item {
+ font-family: var(--cs-font-mono) !important;
+ font-size: 11.5px !important;
+ font-weight: 600 !important;
+ letter-spacing: 0.06em !important;
+ text-transform: uppercase !important;
+ color: var(--cs-ink-mute) !important;
+ padding: 10px 18px !important;
+ border-radius: 6px 6px 0 0 !important;
+ border: none !important;
+ border-bottom: 2px solid transparent !important;
+ white-space: nowrap !important;
+ flex-shrink: 0 !important;
+ transition:
+ color 0.12s,
+ background 0.12s,
+ border-color 0.12s !important;
+ margin-bottom: -1px !important;
+}
+
+.tabs__item:hover {
+ color: var(--cs-ink-dim) !important;
+ background: color-mix(in srgb, var(--cs-ink) 4%, transparent) !important;
+}
+
+/* Active tab */
+.tabs__item--active {
+ color: var(--cs-orange) !important;
+ background: color-mix(in srgb, var(--cs-orange) 6%, transparent) !important;
+ border-bottom-color: var(--cs-orange) !important;
+}
+
+/* Tab panel container */
+.tabs-container [role="tabpanel"] {
+ padding: 20px 0 !important;
+}
+
+/* Tab panel content — scope to div only so li.tabs__item buttons are not zeroed */
+div[class*="tabItem"] {
+ padding: 0 !important;
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 6. BUTTON ANIMATIONS
+ ═══════════════════════════════════════════════════════════════════ */
+.cs-btn {
+ transition:
+ transform 0.12s ease,
+ filter 0.12s ease,
+ box-shadow 0.12s ease !important;
+ will-change: transform;
+}
+
+.cs-btn:hover {
+ transform: translateY(-1px) !important;
+ filter: brightness(1.08) !important;
+ text-decoration: none !important;
+ color: inherit !important;
+}
+
+.cs-btn:active {
+ transform: translateY(1px) !important;
+ filter: brightness(0.94) !important;
+ transition-duration: 0.06s !important;
+}
+
+/* ═══════════════════════════════════════════════════════════════════
+ 7. PAGINATION NAV (prev / next at bottom of doc pages)
+ ═══════════════════════════════════════════════════════════════════ */
+.pagination-nav {
+ gap: 14px !important;
+}
+
+.pagination-nav__link {
+ background: var(--cs-surface) !important;
+ border: 1px solid var(--cs-border) !important;
+ border-radius: 10px !important;
+ padding: 16px 20px !important;
+ text-decoration: none !important;
+ transition:
+ border-color 0.15s,
+ background 0.15s !important;
+ display: flex !important;
+ flex-direction: column !important;
+ gap: 4px !important;
+}
+
+.pagination-nav__link:hover {
+ border-color: color-mix(in srgb, var(--cs-orange) 45%, transparent) !important;
+ background: var(--cs-surface-2) !important;
+ text-decoration: none !important;
+}
+
+.pagination-nav__sublabel {
+ font-family: var(--cs-font-mono) !important;
+ font-size: 10px !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.14em !important;
+ font-weight: 600 !important;
+ color: var(--cs-ink-mute) !important;
+}
+
+.pagination-nav__label {
+ font-size: 14px !important;
+ font-weight: 600 !important;
+ color: var(--cs-ink) !important;
+ line-height: 1.3 !important;
+}
+
+.pagination-nav__link:hover .pagination-nav__label {
+ color: var(--cs-orange) !important;
+}
diff --git a/crowdsec-docs/src/css/crowdsec-tokens.css b/crowdsec-docs/src/css/crowdsec-tokens.css
new file mode 100644
index 000000000..dc7810c01
--- /dev/null
+++ b/crowdsec-docs/src/css/crowdsec-tokens.css
@@ -0,0 +1,377 @@
+/* ─────────────────────────────────────────────────────────────────────────────
+ CrowdSec Design Tokens
+ Single source of truth for all colors. All component CSS uses var(--cs-*).
+ No hex colors are allowed in component files.
+───────────────────────────────────────────────────────────────────────────── */
+
+/* DARK — default */
+:root,
+[data-theme="dark"] {
+ /* Surfaces */
+ --cs-bg: #0a1120;
+ --cs-bg-soft: #0e1729;
+ --cs-surface: #111b30;
+ --cs-surface-2: #162238;
+ --cs-border: rgba(148, 163, 184, 0.1);
+ --cs-border-hi: rgba(148, 163, 184, 0.18);
+
+ /* Text */
+ --cs-ink: #f1f5f9;
+ --cs-ink-dim: #94a3b8;
+ --cs-ink-mute: #64748b;
+
+ /* Brand + path colors */
+ --cs-orange: #f9b124;
+ --cs-teal: #34d399;
+ --cs-violet: #a78bfa;
+ --cs-blue: #60a5fa;
+ --cs-pink: #f472b6;
+
+ /* Button text: dark on bright/light bg in dark mode */
+ --cs-btn-text: #0a1120;
+
+ /* Tinted soft backgrounds via color-mix */
+ --cs-orange-soft: color-mix(in srgb, var(--cs-orange) 12%, transparent);
+ --cs-teal-soft: color-mix(in srgb, var(--cs-teal) 12%, transparent);
+ --cs-violet-soft: color-mix(in srgb, var(--cs-violet) 14%, transparent);
+ --cs-blue-soft: color-mix(in srgb, var(--cs-blue) 12%, transparent);
+ --cs-pink-soft: color-mix(in srgb, var(--cs-pink) 12%, transparent);
+
+ /* Type */
+ --cs-font-sans: "Instrument Sans", system-ui, -apple-system, sans-serif;
+ --cs-font-mono: "JetBrains Mono", "Roboto Mono", ui-monospace, Menlo, monospace;
+
+ /* ── Infima / Docusaurus bridge ──────────────────────────────────────── */
+ --ifm-background-color: var(--cs-bg);
+ --ifm-background-surface-color: var(--cs-bg-soft);
+
+ --ifm-color-primary: var(--cs-orange);
+ --ifm-color-primary-dark: #e2890c;
+ --ifm-color-primary-darker: #c77808;
+ --ifm-color-primary-darkest: #a76406;
+ --ifm-color-primary-light: #fbbe45;
+ --ifm-color-primary-lighter: #fcc85f;
+ --ifm-color-primary-lightest: #fdd988;
+
+ --ifm-color-content: var(--cs-ink);
+ --ifm-color-content-secondary: var(--cs-ink-dim);
+
+ --ifm-toc-border-color: var(--cs-border);
+
+ --ifm-menu-color: var(--cs-ink-dim);
+ --ifm-menu-color-active: var(--cs-orange);
+ --ifm-menu-color-background-active: var(--cs-orange-soft);
+ --ifm-menu-color-background-hover: var(--cs-orange-soft);
+
+ --ifm-font-family-base: var(--cs-font-sans);
+ --ifm-font-family-monospace: var(--cs-font-mono);
+ --ifm-heading-font-family: var(--cs-font-sans);
+
+ --ifm-link-color: var(--cs-orange);
+ --ifm-link-hover-color: var(--cs-orange);
+
+ --ifm-code-background: var(--cs-bg-soft);
+ --ifm-navbar-background-color: color-mix(in srgb, var(--cs-bg) 85%, transparent);
+
+ --ifm-hr-border-color: var(--cs-border-hi);
+ --ifm-blockquote-border-color: var(--cs-orange);
+
+ --ifm-tabs-color-active: var(--cs-orange);
+ --ifm-tabs-color-active-border: var(--cs-orange);
+
+ --ifm-breadcrumb-color-active: var(--cs-orange);
+ --ifm-breadcrumb-separator-color: var(--cs-ink-mute);
+}
+
+/* LIGHT — inverted surfaces, adjusted path colors for AA contrast */
+[data-theme="light"] {
+ /* Surfaces */
+ --cs-bg: #fafaf7;
+ --cs-bg-soft: #ffffff;
+ --cs-surface: #ffffff;
+ --cs-surface-2: #f4f2ec;
+ --cs-border: rgba(15, 23, 42, 0.08);
+ --cs-border-hi: rgba(15, 23, 42, 0.14);
+
+ /* Text */
+ --cs-ink: #0f172a;
+ --cs-ink-dim: #475569;
+ --cs-ink-mute: #64748b;
+
+ /* Brand + path — vibrant enough to carry white text (AA large text) */
+ --cs-orange: #d4890a;
+ --cs-teal: #0a9960;
+ --cs-violet: #7c5ce8;
+ --cs-blue: #2b72e5;
+ --cs-pink: #e03880;
+
+ /* Button text: white on dark/vibrant bg in light mode */
+ --cs-btn-text: #ffffff;
+
+ --cs-orange-soft: color-mix(in srgb, var(--cs-orange) 10%, transparent);
+ --cs-teal-soft: color-mix(in srgb, var(--cs-teal) 10%, transparent);
+ --cs-violet-soft: color-mix(in srgb, var(--cs-violet) 10%, transparent);
+ --cs-blue-soft: color-mix(in srgb, var(--cs-blue) 10%, transparent);
+ --cs-pink-soft: color-mix(in srgb, var(--cs-pink) 10%, transparent);
+
+ /* ── Infima / Docusaurus bridge ──────────────────────────────────────── */
+ --ifm-background-color: var(--cs-bg);
+ --ifm-background-surface-color: var(--cs-bg-soft);
+
+ --ifm-color-primary: var(--cs-orange);
+ --ifm-color-primary-dark: #a76406;
+ --ifm-color-primary-darker: #8f5605;
+ --ifm-color-primary-darkest: #6e4204;
+ --ifm-color-primary-light: #e2890c;
+ --ifm-color-primary-lighter: #f09920;
+ --ifm-color-primary-lightest: #f9b124;
+
+ --ifm-color-content: var(--cs-ink);
+ --ifm-color-content-secondary: var(--cs-ink-dim);
+
+ --ifm-toc-border-color: var(--cs-border);
+
+ --ifm-menu-color: var(--cs-ink-dim);
+ --ifm-menu-color-active: var(--cs-orange);
+ --ifm-menu-color-background-active: var(--cs-orange-soft);
+ --ifm-menu-color-background-hover: var(--cs-orange-soft);
+
+ --ifm-font-family-base: var(--cs-font-sans);
+ --ifm-font-family-monospace: var(--cs-font-mono);
+ --ifm-heading-font-family: var(--cs-font-sans);
+
+ --ifm-link-color: var(--cs-orange);
+ --ifm-link-hover-color: var(--cs-orange);
+
+ --ifm-code-background: var(--cs-surface-2);
+ --ifm-navbar-background-color: var(--cs-bg-soft);
+
+ --ifm-hr-border-color: var(--cs-border-hi);
+ --ifm-blockquote-border-color: var(--cs-orange);
+
+ --ifm-tabs-color-active: var(--cs-orange);
+ --ifm-tabs-color-active-border: var(--cs-orange);
+
+ --ifm-breadcrumb-color-active: var(--cs-orange);
+ --ifm-breadcrumb-separator-color: var(--cs-ink-mute);
+}
+
+/* ── Sidebar ──────────────────────────────────────────────────────────────── */
+
+/* Container background — no border, no dividers, spacing does the job */
+.theme-doc-sidebar-container,
+.theme-doc-sidebar-container aside {
+ background: var(--cs-bg-soft) !important;
+ border-right: 1px solid var(--cs-border) !important;
+ box-shadow: none !important;
+}
+
+.theme-doc-sidebar-item-link hr {
+ background-color: var(--cs-border-hi) !important;
+ margin: 0.25rem 0 !important;
+}
+
+/* All menu links: base style */
+.menu__link {
+ color: var(--cs-ink-dim);
+ font-size: 13.5px;
+ font-weight: 500;
+ border-radius: 7px;
+ padding: 7px 10px;
+ border-left: 2px solid transparent;
+ transition:
+ color 0.12s,
+ background 0.12s;
+}
+
+.menu__link:hover {
+ color: var(--cs-ink);
+ background: color-mix(in srgb, var(--cs-ink) 5%, transparent);
+}
+
+/* Active non-category link */
+.menu__link--active:not(.menu__link--sublist) {
+ color: var(--cs-orange) !important;
+ background: color-mix(in srgb, var(--cs-orange) 14%, transparent) !important;
+ border-left: 2px solid var(--cs-orange);
+ padding-left: calc(10px - 2px);
+ font-weight: 600;
+}
+
+/* Top-level category labels: eyebrow mono */
+.menu__list > .menu__list-item > .menu__list-item-collapsible > .menu__link--sublist {
+ font-family: var(--cs-font-mono);
+ font-size: 10.5px;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--cs-ink-mute);
+ font-weight: 600;
+ padding: 8px 10px 6px;
+ border-radius: 0;
+ border-left: none;
+ background: transparent !important;
+}
+
+.menu__list > .menu__list-item > .menu__list-item-collapsible > .menu__link--sublist:hover {
+ background: transparent !important;
+ color: var(--cs-ink-dim);
+}
+
+.menu__list > .menu__list-item > .menu__list-item-collapsible > .menu__link--sublist.menu__link--active {
+ color: var(--cs-orange);
+ background: transparent !important;
+}
+
+/* ── Sublist (expandable category) links — same look as regular links ──── */
+/* Reset any Infima special treatment on .menu__link--sublist */
+.menu__link--sublist {
+ font-family: var(--cs-font-sans) !important;
+ font-size: 13.5px !important;
+ font-weight: 500 !important;
+ text-transform: none !important;
+ letter-spacing: normal !important;
+ color: var(--cs-ink-dim) !important;
+}
+
+/* Remove Infima's built-in CSS caret for sublist — we use the React chevron */
+.menu__link--sublist-caret:after,
+.menu__link--sublist:after {
+ display: none !important;
+}
+
+/* Nested items: slightly smaller */
+.menu__list .menu__list .menu__link {
+ font-size: 13px;
+ padding-left: 14px;
+}
+
+/* ── Breadcrumb: transparent bg, matches page background ──────────────── */
+nav.theme-doc-breadcrumbs {
+ background: transparent !important;
+}
+
+/* ── Dropdown menu ────────────────────────────────────────────────────────── */
+.dropdown__menu {
+ background: var(--cs-surface) !important;
+ border: 1px solid var(--cs-border-hi) !important;
+ border-radius: 10px !important;
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35) !important;
+ padding: 6px !important;
+ min-width: 200px;
+}
+
+/* items inside the dropdown: only class 'dropdown__link', NOT navbar__link */
+.dropdown__link,
+.dropdown__menu a,
+.dropdown__menu .navbar__link {
+ color: var(--cs-ink-dim) !important;
+ font-size: 12px !important;
+ font-weight: 500 !important;
+ border-radius: 6px;
+ padding: 7px 12px !important;
+ transition:
+ color 0.12s,
+ background 0.12s;
+}
+
+.dropdown__link:hover,
+.dropdown__menu a:hover {
+ color: var(--cs-orange) !important;
+ background: color-mix(in srgb, var(--cs-orange) 12%, transparent) !important;
+}
+
+.dropdown__link--active,
+.dropdown__link--active:hover {
+ color: var(--cs-orange) !important;
+ background: color-mix(in srgb, var(--cs-orange) 12%, transparent) !important;
+}
+
+[data-theme="light"] .dropdown__menu {
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12) !important;
+}
+
+/* ── Secondary navbar (breadcrumb bar) ────────────────────────────────────── */
+/* Target via attribute selector to be resilient to CSS module hash changes */
+[class*="secondaryNavbar_"] {
+ background: var(--cs-bg-soft) !important;
+ border-bottom: 1px solid var(--cs-border) !important;
+ box-shadow: none !important;
+}
+
+/* UI-level links (navbar, TOC, breadcrumb) follow --ifm-link-color (orange) */
+a {
+ color: var(--ifm-link-color);
+}
+
+/* All doc prose links use the brand primary purple.
+ Components with inline-style color (buttons, cards) override this
+ naturally since inline styles beat class-based CSS. */
+.theme-doc-markdown a,
+article a {
+ color: rgb(var(--primary));
+}
+
+.theme-doc-markdown a:hover,
+article a:hover {
+ color: rgb(var(--primary));
+ opacity: 0.8;
+ text-decoration: underline;
+}
+
+/* Preserve admonition body link tone color */
+.cs-admon__content a,
+.cs-admon__content a:hover {
+ color: var(--tone) !important;
+ opacity: 1 !important;
+}
+
+/* Table of contents active link */
+.table-of-contents__link--active {
+ color: var(--cs-orange) !important;
+ font-weight: 600;
+ border-left-color: var(--cs-orange) !important;
+}
+
+/* Tabs active state */
+.tabs__item--active {
+ border-bottom-color: var(--cs-orange) !important;
+ color: var(--cs-orange) !important;
+}
+
+/* ── Footer link columns (rendered inside FooterLayout by Docusaurus) ──────── */
+.footer__col {
+ padding: 0;
+}
+
+.footer__title {
+ font-family: var(--cs-font-mono) !important;
+ font-size: 10.5px !important;
+ letter-spacing: 0.14em !important;
+ text-transform: uppercase !important;
+ color: var(--cs-ink-mute) !important;
+ font-weight: 600 !important;
+ margin-bottom: 14px !important;
+}
+
+.footer__item {
+ margin-bottom: 8px;
+ list-style: none;
+}
+
+.footer__link-item {
+ color: var(--cs-ink-dim) !important;
+ font-size: 13.5px !important;
+ font-weight: 400;
+ text-decoration: none;
+ transition: color 0.12s;
+ line-height: 1.5;
+}
+
+.footer__link-item:hover {
+ color: var(--cs-orange) !important;
+ text-decoration: none;
+}
+
+.footer__copyright {
+ display: none; /* handled by FooterLayout */
+}
diff --git a/crowdsec-docs/src/css/crowdsec-typography.css b/crowdsec-docs/src/css/crowdsec-typography.css
new file mode 100644
index 000000000..3b32e3ce8
--- /dev/null
+++ b/crowdsec-docs/src/css/crowdsec-typography.css
@@ -0,0 +1,110 @@
+/* ─────────────────────────────────────────────────────────────────────────────
+ CrowdSec Typography System
+ Applies to doc pages via .theme-doc-markdown. No hex colors — uses --cs-* vars.
+───────────────────────────────────────────────────────────────────────────── */
+
+/* ── Global font size reduction (from 16px default to 15px) ────────────── */
+:root {
+ --ifm-font-size-base: 15px;
+}
+
+/* ── Base prose container ───────────────────────────────────────────────── */
+.theme-doc-markdown {
+ counter-reset: cs-section;
+ font-size: 14.5px;
+ line-height: 1.65;
+}
+
+.theme-doc-markdown p,
+.theme-doc-markdown li {
+ font-size: 14.5px;
+ line-height: 1.65;
+}
+
+.theme-doc-markdown > p,
+.theme-doc-markdown > ul > li,
+.theme-doc-markdown > ol > li {
+ max-width: 720px;
+ line-height: 1.65;
+}
+
+/* ── Headings ───────────────────────────────────────────────────────────── */
+article h1,
+.theme-doc-markdown h1 {
+ font-size: 44px;
+ font-weight: 700;
+ letter-spacing: -0.025em;
+ text-wrap: balance;
+ line-height: 1.1;
+ margin-bottom: 0.5rem;
+}
+
+article h2,
+.theme-doc-markdown h2 {
+ font-size: 28px;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ line-height: 1.2;
+}
+
+article h3,
+.theme-doc-markdown h3 {
+ font-size: 18.5px;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ line-height: 1.3;
+}
+
+/* ── § NN auto-counter on h2 ────────────────────────────────────────────── */
+.theme-doc-markdown > h2 {
+ counter-increment: cs-section;
+ display: flex;
+ align-items: baseline;
+ gap: 14px;
+}
+
+.theme-doc-markdown > h2::before {
+ content: counter(cs-section, decimal-leading-zero);
+ font: 600 12px / 1 var(--cs-font-mono);
+ color: var(--cs-orange);
+ letter-spacing: 0.16em;
+ flex-shrink: 0;
+ opacity: 0.8;
+}
+
+/* ── Lede paragraph (first
after the page header) ───────────────────── */
+.theme-doc-markdown > p:first-of-type {
+ font-size: 16px;
+ color: var(--cs-ink-dim);
+ line-height: 1.65;
+ max-width: 720px;
+}
+
+/* ── Eyebrow element (rendered by DocItem/Content swizzle) ──────────────── */
+.cs-eyebrow {
+ font-family: var(--cs-font-mono);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.18em;
+ color: var(--cs-orange);
+ font-weight: 600;
+ margin-bottom: 8px;
+ display: block;
+}
+
+/* ── Mobile overrides ───────────────────────────────────────────────────── */
+@media (max-width: 768px) {
+ article h1,
+ .theme-doc-markdown h1 {
+ font-size: 32px;
+ }
+
+ article h2,
+ .theme-doc-markdown h2 {
+ font-size: 22px;
+ }
+
+ .theme-doc-markdown > h2::before {
+ font-size: 10px;
+ }
+}
diff --git a/crowdsec-docs/src/css/custom.css b/crowdsec-docs/src/css/custom.css
index 1cb496aab..1d313ed97 100644
--- a/crowdsec-docs/src/css/custom.css
+++ b/crowdsec-docs/src/css/custom.css
@@ -5,6 +5,10 @@
@import "tailwindcss/utilities";
@import url("colors.css"); /* SHOULD BE ON TOP */
+@import url("crowdsec-tokens.css");
+@import url("crowdsec-typography.css");
+@import url("crowdsec-algolia.css");
+@import url("crowdsec-components.css");
@import url("alerts.css");
@import url("code.css");
@import url("navbar.css");
@@ -27,32 +31,10 @@ body,
}
.theme-doc-sidebar-container {
- @apply bg-card;
+ background: var(--cs-bg-soft) !important;
}
-html[data-theme="light"] .menu__link--active {
- --ifm-menu-color-active: rgb(var(--primary));
-}
-html[data-theme="light"] .navbar-sidebar__item .menu__list .menu__link--active {
- --ifm-menu-color-active: rgb(var(--secondary));
-}
-
-html[data-theme="light"] .tabs__item--active {
- --ifm-color-primary: rgb(var(--primary));
- --ifm-tabs-color-active: rgb(var(--primary));
- border-bottom: 2px solid rgb(var(--primary));
-}
-html[data-theme="light"] .table-of-contents__link:hover {
- --ifm-color-primary: rgb(var(--primary));
-}
-html[data-theme="light"] .breadcrumbs__link {
- --ifm-breadcrumb-color-active: rgb(var(--primary));
- --ifm-link-hover-color: rgb(var(--primary));
-}
-html[data-theme="light"] .dropdown__link--active,
-html[data-theme="light"] .dropdown__link--active:hover {
- --ifm-link-color: rgb(var(--primary));
-}
+/* Infima active state overrides are now handled by crowdsec-tokens.css */
html[data-theme="dark"] {
--docusaurus-highlighted-code-line-bg: rgba(255, 255, 255, 0.1);
@@ -189,11 +171,11 @@ div.markdown .doc-quick-strip__pill {
}
a {
- @apply text-primary;
+ color: var(--ifm-link-color);
}
blockquote {
- --ifm-blockquote-border-color: rgb(var(--primary));
+ --ifm-blockquote-border-color: var(--cs-orange);
--ifm-blockquote-background-color: transparent;
}
@@ -209,7 +191,7 @@ div[class^="announcementBar"] {
@media screen and (max-width: 996px) {
:root {
- --ifm-font-size-base: 1rem;
+ --ifm-font-size-base: 14px;
}
article header h1 {
font-size: 1.5rem !important;
@@ -221,7 +203,7 @@ div[class^="announcementBar"] {
@media screen and (min-width: 997px) {
:root {
- --ifm-font-size-base: 1rem;
+ --ifm-font-size-base: 15px;
}
article header h1 {
font-size: 2rem !important;
@@ -383,43 +365,7 @@ html:has(.homepage-search) [class*="navbarSearchContainer"] {
}
}
-/* Algolia DocSearch modal theming */
-[data-theme="light"] .DocSearch {
- --docsearch-primary-color: rgb(var(--primary));
- --docsearch-text-color: rgb(var(--foreground));
- --docsearch-muted-color: rgb(var(--muted-foreground));
- --docsearch-container-background: rgb(var(--color-gray-900) / 0.55);
- --docsearch-modal-background: rgb(var(--card));
- --docsearch-searchbox-background: rgb(var(--input));
- --docsearch-searchbox-focus-background: rgb(var(--card));
- --docsearch-hit-color: rgb(var(--foreground));
- --docsearch-hit-active-color: rgb(var(--primary-foreground));
- --docsearch-hit-background: rgb(var(--background));
- --docsearch-footer-background: rgb(var(--muted));
- --docsearch-key-gradient: linear-gradient(180deg, rgb(var(--background)), rgb(var(--muted)));
- --docsearch-key-shadow: 0 1px 0 rgb(var(--border)), 0 1px 3px rgba(0, 0, 0, 0.15);
-}
-
-[data-theme="light"] .DocSearch-Commands-Key {
- background: rgb(var(--color-gray-200));
- box-shadow: 0 1px 0 rgb(var(--color-gray-300));
-}
-
-[data-theme="dark"] .DocSearch {
- --docsearch-primary-color: rgb(var(--primary));
- --docsearch-text-color: rgb(var(--foreground));
- --docsearch-muted-color: rgb(var(--muted-foreground));
- --docsearch-container-background: rgb(var(--color-gray-950) / 0.72);
- --docsearch-modal-background: rgb(var(--card));
- --docsearch-searchbox-background: rgb(var(--input));
- --docsearch-searchbox-focus-background: rgb(var(--card));
- --docsearch-hit-color: rgb(var(--foreground));
- --docsearch-hit-active-color: rgb(var(--primary-foreground));
- --docsearch-hit-background: rgb(var(--background));
- --docsearch-footer-background: rgb(var(--background));
- --docsearch-key-gradient: linear-gradient(180deg, rgb(var(--background)), rgb(var(--muted)));
- --docsearch-key-shadow: 0 1px 0 rgb(var(--border)), 0 1px 3px rgba(0, 0, 0, 0.35);
-}
+/* Algolia DocSearch modal theming is in crowdsec-algolia.css */
/* Quick guide specific CSS /u/getting_started/... */
.sideBarItemRecommended a::after {
diff --git a/crowdsec-docs/src/css/navbar.css b/crowdsec-docs/src/css/navbar.css
index f50ce34da..27d82dc8f 100644
--- a/crowdsec-docs/src/css/navbar.css
+++ b/crowdsec-docs/src/css/navbar.css
@@ -1,33 +1,86 @@
.navbar {
- @apply border-b border-border border-solid;
+ background: rgba(8, 14, 26, 0.85) !important;
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ border-bottom: 1px solid var(--cs-border) !important;
+}
+
+[data-theme="light"] .navbar {
+ background: rgba(250, 250, 247, 0.9) !important;
+ border-bottom: 1px solid var(--cs-border) !important;
}
.navbar__link {
- --ifm-navbar-link-hover-color: rgb(var(--primary));
+ --ifm-navbar-link-hover-color: var(--cs-orange);
+ color: var(--cs-ink-dim);
+ font-size: 13.5px;
+ font-weight: 500;
+ border-radius: 6px;
+ padding: 6px 10px;
+ transition:
+ color 0.15s,
+ background 0.15s;
+}
+
+.navbar__link:hover,
+.navbar__link--active {
+ color: var(--cs-orange) !important;
+ background: color-mix(in srgb, var(--cs-orange) 12%, transparent);
}
.navbar,
.navbar-sidebar {
- @apply bg-card text-foreground border-border/80;
- --ifm-navbar-link-color: rgb(var(--foreground));
- --ifm-menu-color: rgb(var(--foreground));
+ --ifm-navbar-link-color: var(--cs-ink-dim);
+ --ifm-menu-color: var(--cs-ink-dim);
}
-.navbar {
- @apply border border-b border-solid;
+/* Logo area */
+.navbar__logo {
+ height: 28px;
}
.navbar-separator {
display: inline-block;
width: 1px;
- height: 24px;
- background: var(--ifm-color-emphasis-300);
- margin: 0 0.75rem;
+ height: 18px;
+ background: var(--cs-border-hi);
+ margin: 0 6px;
vertical-align: middle;
+ align-self: center;
}
nav.theme-doc-breadcrumbs {
- display: none;
+ font-family: var(--cs-font-mono);
+ font-size: 12px;
+ letter-spacing: 0.04em;
+ color: var(--cs-ink-mute);
+ margin-bottom: 20px;
+}
+
+.breadcrumbs__item {
+ font-size: 12px;
+}
+
+.breadcrumbs__link {
+ color: var(--cs-ink-mute) !important;
+ background: transparent !important;
+ padding: 4px 6px;
+ border-radius: 5px;
+ transition: color 0.12s;
+}
+
+.breadcrumbs__link:hover {
+ color: var(--cs-ink) !important;
+}
+
+.breadcrumbs__item--active .breadcrumbs__link {
+ color: var(--cs-ink) !important;
+ font-weight: 500;
+}
+
+.breadcrumbs__separator {
+ color: var(--cs-ink-mute);
+ opacity: 0.5;
}
/** Patch some colors **/
@@ -100,17 +153,19 @@ html[data-theme="light"] .navbar-sidebar__item > .menu__list .menu__caret::befor
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 275.05 249.82' %3E%3Cstyle%3E%3C!%5BCDATA%5B.C%7Bletter-spacing:-0.04em%7D%5D%5D%3E%3C/style%3E%3Cpath d='M39.26 178.8a20.79 20.79 0 0 1-7 4.92 24 24 0 0 1-9.76 1.84 23.7 23.7 0 0 1-9.86-1.9 20.78 20.78 0 0 1-7-5 21.06 21.06 0 0 1-4.21-7.07A24 24 0 0 1 0 163.47a24.34 24.34 0 0 1 1.41-8.14 21 21 0 0 1 4.21-7.09 20.78 20.78 0 0 1 7-5 23.7 23.7 0 0 1 9.86-1.9 23.64 23.64 0 0 1 9.76 1.87 20.72 20.72 0 0 1 7 5l-5.1 4.24a14.52 14.52 0 0 0-4.92-3.57 16.37 16.37 0 0 0-6.88-1.35 14.84 14.84 0 0 0-14 8.25 17.57 17.57 0 0 0 0 15.33 14.85 14.85 0 0 0 14 8.24 16.65 16.65 0 0 0 6.79-1.29 14.39 14.39 0 0 0 4.87-3.44l5.09 4.24zm2.45-25.49h6.21v3.13a9.62 9.62 0 0 1 2.33-1.81 13.23 13.23 0 0 1 2.89-1.23 18.62 18.62 0 0 1 3.14-.65 16.42 16.42 0 0 1 3.13-.06v6a15.17 15.17 0 0 0-5.65.37 7.41 7.41 0 0 0-3.51 2.15 8.42 8.42 0 0 0-1.81 3.56 19.24 19.24 0 0 0-.52 4.67v15.06h-6.21zM75 185.19a16.52 16.52 0 0 1-9.15-2.39 15.31 15.31 0 0 1-5.5-6 17.42 17.42 0 0 1-1.84-7.8 16.7 16.7 0 0 1 1.81-7.8 15.59 15.59 0 0 1 5.5-6 16.39 16.39 0 0 1 9.18-2.4 16.42 16.42 0 0 1 9.19 2.4 15.67 15.67 0 0 1 5.5 6 16.7 16.7 0 0 1 1.81 7.8 17.55 17.55 0 0 1-1.84 7.8 15.39 15.39 0 0 1-5.5 6 16.53 16.53 0 0 1-9.16 2.39zm0-27.77a9.23 9.23 0 0 0-5.71 1.72 10.8 10.8 0 0 0-3.41 4.27 14.13 14.13 0 0 0 0 11.12 10.73 10.73 0 0 0 3.41 4.27 10.35 10.35 0 0 0 11.43 0 10.82 10.82 0 0 0 3.41-4.27 14.24 14.24 0 0 0 0-11.12 10.89 10.89 0 0 0-3.41-4.27 9.26 9.26 0 0 0-5.72-1.72zm49 19.48l6.51-23.53h7.31l-10.2 31.21h-7.31l-6.45-24-6.45 24h-7.31l-10.2-31.21h7.31l6.51 23.53 6.52-23.53h7.12zm39.79-35.33h6.27v43h-6.27v-4.43a12.91 12.91 0 0 1-4.7 3.81 15 15 0 0 1-11.61.56 15.41 15.41 0 0 1-9-8.3 19 19 0 0 1 0-14.5 15.38 15.38 0 0 1 9-8.29 15.09 15.09 0 0 1 11.63.55 12.86 12.86 0 0 1 4.71 3.81zm-10.23 15.85a9.82 9.82 0 0 0-4.5 1 9.61 9.61 0 0 0-3.22 2.62 11.21 11.21 0 0 0-1.93 3.71 14.37 14.37 0 0 0 0 8.48 11.36 11.36 0 0 0 1.93 3.72 9.7 9.7 0 0 0 3.22 2.61 10.82 10.82 0 0 0 9 0 9.7 9.7 0 0 0 3.22-2.61 11.36 11.36 0 0 0 1.93-3.72 14.36 14.36 0 0 0 0-8.48 11.21 11.21 0 0 0-1.93-3.71 9.61 9.61 0 0 0-3.22-2.62 9.85 9.85 0 0 0-4.5-1zm37.26-16.09a28.52 28.52 0 0 1 9.22 1.56 33 33 0 0 1 7.68 3.9l-3.44 3.94-.74.86a21.17 21.17 0 0 0-7-3.78 18.59 18.59 0 0 0-6.45-.83 13 13 0 0 0-5.19 1.32 7.92 7.92 0 0 0-3.19 2.7 3.84 3.84 0 0 0-.49 3.26 5.56 5.56 0 0 0 2.88 3 30.3 30.3 0 0 0 6.05 2.49q3.42 1 6.73 2.21a32.23 32.23 0 0 1 6 2.89 10.15 10.15 0 0 1 4 4.51 11.41 11.41 0 0 1 1 3.84 10.55 10.55 0 0 1-.58 4.21 8.33 8.33 0 0 1-1.48 2.67 11.45 11.45 0 0 1-2.27 2.12 17.44 17.44 0 0 1-5.84 2.58 28.66 28.66 0 0 1-6.2.86 33.14 33.14 0 0 1-5.32-.4 24.81 24.81 0 0 1-4.76-1.23 25.47 25.47 0 0 1-4.48-2.15 31.86 31.86 0 0 1-4.49-3.28l3.38-3.87.86-1a24.25 24.25 0 0 0 7.56 4.77 23.32 23.32 0 0 0 8.41 1.32 10.3 10.3 0 0 0 4.64-1.08 8.57 8.57 0 0 0 3.29-2.64 4.07 4.07 0 0 0 .71-3.35c-.27-1.19-1.26-2.29-3-3.32a22.84 22.84 0 0 0-4.27-1.87l-5.16-1.66-5.29-1.81a23.21 23.21 0 0 1-4.67-2.37 11.79 11.79 0 0 1-3.31-3.25 7.6 7.6 0 0 1-1.2-4.52 10.62 10.62 0 0 1 1.5-5.47 13.24 13.24 0 0 1 3.75-3.93 17.11 17.11 0 0 1 5.19-2.39 21.53 21.53 0 0 1 5.84-.8zm35.88 39.13a9.5 9.5 0 0 0 5.13-1.32 9.93 9.93 0 0 0 3.35-3.41l4.54 3.5a14.91 14.91 0 0 1-5.28 4.27 17.06 17.06 0 0 1-7.74 1.63 18 18 0 0 1-6.64-1.14 15.19 15.19 0 0 1-4.94-3.07 14.82 14.82 0 0 1-3.26-4.45 17.51 17.51 0 0 1 0-15.12 14.82 14.82 0 0 1 3.26-4.45 15.36 15.36 0 0 1 4.94-3.07 19.88 19.88 0 0 1 13.27 0 15.3 15.3 0 0 1 4.95 3.07 14.82 14.82 0 0 1 3.26 4.45 17.53 17.53 0 0 1 1.56 9.9h-26.41a13.65 13.65 0 0 0 1.07 3.5 10.56 10.56 0 0 0 2 2.95 9 9 0 0 0 3 2 10.16 10.16 0 0 0 4 .74zm10.14-13.88a14 14 0 0 0-1.08-3.51 10.51 10.51 0 0 0-2-2.94 8.89 8.89 0 0 0-3-2 11 11 0 0 0-7.92 0 8.89 8.89 0 0 0-3 2 10.51 10.51 0 0 0-2 2.94 13.6 13.6 0 0 0-1.07 3.51h20.15zm32.93 16.89a17.1 17.1 0 0 1-7.87 1.66 16.44 16.44 0 0 1-9.15-2.4 15.22 15.22 0 0 1-5.5-6 17.42 17.42 0 0 1-1.84-7.8 16.67 16.67 0 0 1 1.81-7.8 15.52 15.52 0 0 1 5.5-6 16.39 16.39 0 0 1 9.18-2.4 17.1 17.1 0 0 1 7.87 1.66 15 15 0 0 1 5.28 4.36l-4.55 3.57a10.1 10.1 0 0 0-3.37-3.54 9.51 9.51 0 0 0-5.23-1.38 9.16 9.16 0 0 0-5.71 1.72 10.8 10.8 0 0 0-3.41 4.27 14.13 14.13 0 0 0 0 11.12 10.8 10.8 0 0 0 3.41 4.27 9.16 9.16 0 0 0 5.71 1.72 9.51 9.51 0 0 0 5.23-1.38 10.15 10.15 0 0 0 3.37-3.53l4.55 3.56a14.91 14.91 0 0 1-5.28 4.32z' fill='black'/%3E%3Cpath d='M110 117.18l-.72 6.65h.06l.66-6.65z' fill='black' /%3E%3Ctext class='C' transform='translate(83.51 237.44)' fill='black' font-size='61' font-family='BellinzoRegularRegular,Bellinzo Regular'%3EHub%3C/text%3E%3Cg fill='black' %3E%3Cpath d='M102.63 63.66a20.63 20.63 0 0 1 1.69 3.48h0a27.11 27.11 0 0 0-2.14-5.92c-1.56-3-3.86-2.86-5.3 0a29.14 29.14 0 0 0-2.14 6 23.85 23.85 0 0 1 1.68-3.55c1.69-2.87 4.39-2.99 6.21-.01zm73.63 0a21.42 21.42 0 0 1 1.74 3.48h0a26.6 26.6 0 0 0-2.14-5.92c-1.55-3-3.85-2.86-5.29 0a29.14 29.14 0 0 0-2.14 6 23.85 23.85 0 0 1 1.68-3.55c1.63-2.87 4.33-2.99 6.15-.01zM140 46.06a27.85 27.85 0 0 1 2.37 5.32 34.92 34.92 0 0 0-2.77-7.78c-1.94-3.72-4.81-3.57-6.61 0a37.88 37.88 0 0 0-2.77 7.9 30.88 30.88 0 0 1 2.36-5.44c1.99-3.57 5.22-3.72 7.42 0zM95 67.82a5.51 5.51 0 0 0 3.59 4.59l.13 4 .46 4.78c.19 1.13.48 1.08.66 0 .25-1.52.32-3.15.46-4.78a39 39 0 0 0 .12-4 5.5 5.5 0 0 0 3.6-4.59 9.08 9.08 0 0 1-9 0z'/%3E%3Cpath d='M168.63 67.82a5.5 5.5 0 0 0 3.6 4.59 39 39 0 0 0 .12 4l.46 4.78c.2 1.13.48 1.08.66 0 .25-1.52.32-3.15.46-4.78l.13-4a5.51 5.51 0 0 0 3.59-4.59 9.08 9.08 0 0 1-9 0zM188 75.94c1 4.27 2.23 13.48 3.5 24.14C190.43 90 189.28 81 188 75.94zm-46.18-24.07a11.36 11.36 0 0 1-11.25 0c.52 2.88 2.27 5.13 4.49 5.71l.15 5.05.58 6c.24 1.41.59 1.35.82 0 .31-1.9.4-3.93.57-6 .14-1.66.18-3.35.16-5.05 2.21-.58 3.97-2.83 4.48-5.71zM98.54 72.41l.13 4 .46 4.78c.19 1.13.48 1.08.66 0 .25-1.52.32-3.15.46-4.78a39 39 0 0 0 .12-4 5.5 5.5 0 0 0 3.6-4.59 9.08 9.08 0 0 1-9 0 5.51 5.51 0 0 0 3.57 4.59zm60.6 19.68c-1.59-13.3-3.15-24.78-4.37-30.1a7.1 7.1 0 0 0 6.87-5.84h0a8.19 8.19 0 0 0 .12-1.11h.2a4.47 4.47 0 0 0 2.74-8.18 7.1 7.1 0 0 0-1.8-13.51 4.47 4.47 0 0 0-3.43-7.22 7.17 7.17 0 0 0 2.87-2.52c1.67-2.61 1.94-14.4-4-22-2.44-3.12-4.83-1.24-5 .89-.47 6.52-.52 11.42-2.22 17a4.46 4.46 0 0 0-7.92-2.05 7.1 7.1 0 0 0-14 0 4.47 4.47 0 0 0-7.94 2c-1.69-5.59-1.75-10.49-2.22-17-.15-2.13-2.54-4-5-.89-5.93 7.58-5.66 19.37-4 22a7.17 7.17 0 0 0 2.87 2.52 4.48 4.48 0 0 0-3.43 7.22 7.1 7.1 0 0 0-1.79 13.51 4.47 4.47 0 0 0 2.73 8.18h.21a6.36 6.36 0 0 0 .11 1.07h0a7.1 7.1 0 0 0 7 5.89h0c-2 8.51-5 32.87-7.43 55.17l-.66 6.65a842.28 842.28 0 0 1 26.64-.42c9 0 17.83.14 26.22.41l-.67-6.27-2.7-25.4zm-14.58-40a5.88 5.88 0 0 1-2.39-1.17c.7 2.78 1.21 5.61 1.93 8.44a45.15 45.15 0 0 1 1.22 7.43c.31 3.86-2.22 7.19-5.39 7.19h-7.47c-3.17 0-5.7-3.33-5.39-7.19a46.23 46.23 0 0 1 1.22-7.43c.66-2.61 1.17-5.23 1.81-7.8a8.31 8.31 0 0 1-3.84 1 44.4 44.4 0 0 1-6.91-.45 4.78 4.78 0 0 1-4-3.37c-.59-1.69-1-3.45-1.59-5.14a14.16 14.16 0 0 0-1.21-2.09 2.41 2.41 0 0 1-.42-.89v-3.86a38.54 38.54 0 0 1 12.17-1.54 27 27 0 0 1 8.8 1.61 10.09 10.09 0 0 0 7.18 0 30 30 0 0 1 13.6-1.44c2.45.22 4.87.76 7.35 1.16v4.08c0 .29-.13.74-.33.82-1 .45-1.11 1.36-1.35 2.25a84.99 84.99 0 0 1-1.41 4.71 5.44 5.44 0 0 1-4.38 3.79 20.06 20.06 0 0 1-9.2-.1zm-29.92 15.64a5.53 5.53 0 0 1-1.13.36 16.1 16.1 0 0 1-7.34-.08 4.61 4.61 0 0 1-1.92-.94l1.55 6.77a36.86 36.86 0 0 1 1 6c.24 3.1-1.78 5.77-4.33 5.77h-6c-2.55 0-4.58-2.67-4.33-5.77a36.86 36.86 0 0 1 1-6l1.45-6.26a6.48 6.48 0 0 1-3.08.79A34.29 34.29 0 0 1 86 68a3.85 3.85 0 0 1-3.22-2.71l-1.28-4.12a10.81 10.81 0 0 0-1-1.67 1.73 1.73 0 0 1-.33-.72v-3.09a30.28 30.28 0 0 1 9.75-1.23 21.25 21.25 0 0 1 7.06 1.29 8.11 8.11 0 0 0 5.76 0 19.87 19.87 0 0 1 2.74-.81 6.47 6.47 0 0 1-.79-7.68 9.11 9.11 0 0 1-2.18-11 5.59 5.59 0 0 0-3-.86 5.7 5.7 0 0 0-5.64 4.88 3.58 3.58 0 0 0-6.37 1.62c-1.35-4.48-1.4-8.41-1.77-13.64-.12-1.7-2-3.21-4-.71-4.75 6.08-4.54 15.53-3.2 17.63a5.86 5.86 0 0 0 2.3 2A3.58 3.58 0 0 0 78 53a5.69 5.69 0 0 0-1.4 10.82 3.59 3.59 0 0 0 2.19 6.56h.17a6 6 0 0 0 .09.86h0A5.69 5.69 0 0 0 84.66 76h0c-1.58 6.82-4 26.36-6 44.24l-.53 5.38c9-.74 18.89-1.31 29.48-1.68l.69-6.94 2.87-24.67 3.47-24.6z'/%3E%3Cpath d='M199.39 58.62a5.7 5.7 0 0 0-4.83-5.62 3.58 3.58 0 0 0-2.75-5.79 5.86 5.86 0 0 0 2.3-2c1.34-2.1 1.55-11.55-3.2-17.63-2-2.5-3.87-1-4 .71-.37 5.23-.41 9.16-1.77 13.64a3.59 3.59 0 0 0-6.36-1.64 5.69 5.69 0 0 0-8.78-3.92 9.1 9.1 0 0 1-2.23 10.9 6.44 6.44 0 0 1-.66 7.54 21.08 21.08 0 0 1 3.54 1 8.11 8.11 0 0 0 5.76 0 24.1 24.1 0 0 1 10.91-1.15c2 .17 3.91.6 5.9.93v3.27c0 .23-.11.59-.26.66-.82.36-.9 1.09-1.09 1.8a63 63 0 0 1-1.13 3.78 4.35 4.35 0 0 1-3.51 3 16.1 16.1 0 0 1-7.34-.08 4.67 4.67 0 0 1-1.92-.94c.56 2.23 1 4.5 1.55 6.77a36.86 36.86 0 0 1 1 6c.25 3.1-1.78 5.77-4.33 5.77h-6c-2.55 0-4.57-2.67-4.33-5.77a36.86 36.86 0 0 1 1-6c.53-2.09.94-4.19 1.45-6.26a6.48 6.48 0 0 1-3.08.79 34.29 34.29 0 0 1-5.54-.35 3.78 3.78 0 0 1-1.84-.74c1.52 9.16 3.53 25.4 6.18 50.05l.29 2.62q.19 1.9.42 3.93c10.62.37 20.57.94 29.58 1.68l-.55-5.07-2.17-20.41c-1.33-10.67-2.6-19.88-3.6-24.15a5.7 5.7 0 0 0 5.51-4.68h0a6.69 6.69 0 0 0 .09-.9h.17a3.59 3.59 0 0 0 2.23-6.54 5.69 5.69 0 0 0 3.39-5.2zm-36.87 65.19h.15q-.33-3.09-.67-6.27l-2.86-25.45 2.71 25.45.67 6.27z'/%3E%3Cpath d='M154.77 62c1.22 5.32 2.78 16.8 4.37 30.1-1.39-12.56-2.82-23.86-4.37-30.1zm-44.45 55.17l-.72 6.65h.06l.66-6.65z'/%3E%3C/g%3E%3C/svg%3E");
}
-/* On wide screens (desktop), remove text content for icon-only display */
+/* Desktop: icon-only — centered in button */
@media (min-width: 1401px) {
.header-roadmap-link:before,
.header-discord-link:before,
.header-github-link:before,
.header-discourse-link:before {
- content: "";
- padding-left: 0.7rem;
- padding-right: 0.8rem;
- background-position: center;
- background-size: 80%;
+ content: "" !important;
+ padding: 0 !important;
+ width: 18px;
+ height: 18px;
+ display: inline-block;
+ background-position: center !important;
+ background-size: 100% !important;
}
}
@@ -124,19 +179,41 @@ html[data-theme="light"] .navbar-sidebar__item > .menu__list .menu__caret::befor
}
.navbar__items--right .navbar__link:hover {
- @apply opacity-60;
+ opacity: 0.7;
}
.navbar {
- @apply whitespace-nowrap;
+ white-space: nowrap;
+}
+
+/* Right icon buttons: centered, equal padding */
+.navbar__items--right .navbar__link {
+ padding: 5px 7px !important;
+ display: inline-flex !important;
+ align-items: center;
+ justify-content: center;
+ height: 32px;
}
.navbar__items--right .navbar__link:before {
- @apply px-3 h-6;
+ /* Below 1401px: icon + text label */
+ padding-right: 0 !important;
+ height: 18px;
+ display: inline-flex;
+ align-items: center;
+ background-size: contain !important;
}
-.navbar__items--right .navbar__link {
- @apply pl-4 pr-0;
+/* Ensure left nav items are vertically centered */
+.navbar__items--left .navbar__link,
+.navbar__items--left .navbar__item {
+ display: inline-flex;
+ align-items: center;
+}
+
+/* Tighten gap around separator */
+.navbar-separator {
+ margin: 0 6px;
}
@media (max-width: 1400px) {
diff --git a/crowdsec-docs/src/pages/index.tsx b/crowdsec-docs/src/pages/index.tsx
index 45f382f39..183c50937 100644
--- a/crowdsec-docs/src/pages/index.tsx
+++ b/crowdsec-docs/src/pages/index.tsx
@@ -1,484 +1,123 @@
-import Link from "@docusaurus/Link";
+import { cilBarChart, cilCompass, cilGlobeAlt, cilRss, cilShieldAlt, cilSpeedometer } from "@coreui/icons";
+import { CIcon } from "@coreui/icons-react";
import Layout from "@theme/Layout";
import SearchBar from "@theme/SearchBar";
-import { ExternalLink } from "lucide-react";
-import React, { useEffect, useState } from "react";
-import { Button } from "../ui/button";
+import { useEffect } from "react";
+import GuidedSetupCard from "../components/docs/GuidedSetupCard";
+import PathCard from "../components/docs/PathCard";
+import PathCards from "../components/docs/PathCards";
+import PathwayRow from "../components/docs/PathwayRow";
+import QuickStrip from "../components/docs/QuickStrip";
+import RunningStrip from "../components/docs/RunningStrip";
-// ── Intent card ──────────────────────────────────────────────────────────────
+/* ── Colour vars — reference CSS tokens (work in dark + light) */
+const CS_ORANGE = "var(--cs-orange)";
+const CS_TEAL = "var(--cs-teal)";
+const CS_VIOLET = "var(--cs-violet)";
+const CS_BLUE = "var(--cs-blue)";
-type IntentCardProps = {
- icon: React.ReactNode;
- title: string;
- desc: string;
- pill: string;
- accent: string;
- href: string;
- aka?: string[];
-};
+const ICON_SM = { width: 16, height: 16 };
-const IntentCard = ({ icon, title, desc, pill, accent, href, aka }: IntentCardProps) => (
- {
- const el = e.currentTarget as HTMLAnchorElement;
- el.style.borderColor = accent;
- el.style.boxShadow = `0 8px 24px ${accent}22, 0 0 0 1px ${accent}`;
- el.style.transform = "translateY(-2px)";
- el.style.borderRadius = "14px";
- }}
- onMouseLeave={(e) => {
- const el = e.currentTarget as HTMLAnchorElement;
- el.style.borderColor = "";
- el.style.boxShadow = "";
- el.style.transform = "";
- }}
- >
-
-
-
- {desc}
-
-
-
- → {pill}
-
-
- {aka && aka.length > 0 && (
-
-
- aka
-
- {aka.map((tag) => (
-
- {tag}
-
- ))}
-
- )}
-
-
-);
-
-// ── Schema / path block ───────────────────────────────────────────────────────
-
-type Step = {
- num: number;
- icon: string;
- title: string;
- desc: string;
- hint?: string;
-};
-
-type SchemaBlockProps = {
- id: string;
- color: string;
- eyebrowIcon: string;
- eyebrow: string;
- title: string;
- ctaLabel: string;
- ctaHref: string;
- steps: Step[];
- open: boolean;
- onToggle: () => void;
-};
-
-const SchemaBlock = ({ id, color, eyebrowIcon, eyebrow, title, ctaLabel, ctaHref, steps, open, onToggle }: SchemaBlockProps) => (
-
- {/* left accent strip */}
-
- {/* subtle radial glow */}
-
-
- {/* header - always visible, clickable to toggle */}
-
-
-
- {eyebrowIcon} {eyebrow}
-
-
{title}
-
-
- e.stopPropagation()}
- style={{
- display: "inline-flex",
- alignItems: "center",
- gap: "6px",
- padding: "8px 16px",
- borderRadius: "8px",
- fontSize: "13px",
- fontWeight: 600,
- background: color,
- color: "#000",
- textDecoration: "none",
- border: "none",
- }}
- >
- {ctaLabel}
-
-
- ▾
-
-
-
-
- {/* collapsible step flow */}
- {open && (
-
- {steps.map((step, i) => (
-
- {i > 0 && (
-
- →
-
- )}
- {step.hint && (
-
- {step.hint}
-
- )}
-
- {step.num}
-
-
{step.icon}
-
{step.title}
-
{step.desc}
-
- ))}
-
- )}
-
-);
-
-// ── Data ──────────────────────────────────────────────────────────────────────
-
-const ORANGE = "#f97316";
-const GREEN = "#22d3a0";
-const BLUE = "#60a5fa";
-
-const intents: IntentCardProps[] = [
+const securityEngineSteps = [
{
- icon:
,
- accent: ORANGE,
- title: "Detect & Block attacks on my servers",
- desc: "Locally identify and ban bad behaving IPs observed in your logs and requests with CrowdSec Detection Scenarios, and Virtual-Patching Collections.",
- pill: "Security Engine",
- href: "/security-engine",
- aka: ["IDPS", "WAF", "CrowdSec FOSS"],
+ title: "Install the Security Engine",
+ desc: "Runs on your server, detects attack patterns in real time. Immediately protected with the Community Blocklist.",
},
{
- icon:
,
- accent: GREEN,
- title: "Push a Blocklists into my firewall, CDN or WAF",
- desc: "You manage network perimeter devices and want a URL to subscribe to. No agent to install.",
- pill: "Blocklist Integration Endpoint",
- href: "/blocklists",
- aka: ["Threat Feeds", "IOC Streams", "Deny-list"],
+ title: "Activate the WAF module",
+ hint: "RECOMMENDED" as const,
+ desc: "Layer in the AppSec component to inspect HTTP traffic and block web exploits.",
},
{
- icon:
,
- accent: BLUE,
- title: "Investigate IPs Behaviors and Enrich Alerts",
- desc: "You're a security analyst or developer who wants IP context, behaviors, CVEs, Aggressivity... In a browser or via REST API.",
- pill: "IP Reputation & CTI",
- href: "/u/cti_api/intro",
- aka: ["IoC Lookup", "Threat Intel"],
+ title: "Subscribe to blocklists",
+ hint: "OPTIONAL" as const,
+ desc: "Add extra curated feeds on top of the built-in detection & community blocklist.",
+ },
+ {
+ title: "Craft your own rules",
+ hint: "OPTIONAL" as const,
+ desc: "Write custom scenarios for your stack, then share them on the Hub.",
+ },
+];
+const blocklistSteps = [
+ {
+ title: "Create an integration endpoint",
+ desc: "Generates a dedicated URL and credentials to serve blocklists to your perimeter devices.",
+ },
+ {
+ title: "Choose blocklists to serve",
+ desc: "Select from curated feeds: scanners, bots, TOR exits, exploits, and more.",
+ },
+ {
+ title: "Plug in as a threat feed",
+ desc: "Point your firewall, CDN, or WAF at the endpoint. No agent to install.",
+ },
+];
+const ctiSteps = [
+ {
+ title: "Look up any IP in the Console",
+ desc: "Get reputation score, behaviors, attack history, and CVE links instantly.",
+ },
+ {
+ title: "Generate a CTI API key",
+ hint: "OPTIONAL" as const,
+ desc: "Unlock programmatic access to 30+ data points per IP detected by the CrowdSec network.",
+ },
+ {
+ title: "Connect to your SIEM/SOAR",
+ hint: "OPTIONAL" as const,
+ desc: "Native integrations for Splunk, Sentinel, QRadar, TheHive, MISP, and more.",
},
];
-const schemas: Omit
[] = [
+const alreadyRunningLinks = [
+ {
+ icon: ,
+ label: "Open the Console",
+ href: "https://app.crowdsec.net",
+ color: CS_ORANGE,
+ external: true,
+ },
{
- id: "schema-engine",
- color: ORANGE,
- eyebrowIcon: "🛡️",
- eyebrow: "Security Engine",
- title: "Detect and block malicious behaviors on your infrastructure",
- ctaLabel: "Install CrowdSec →",
- ctaHref: "/security-engine",
- steps: [
- {
- num: 1,
- icon: "⚡",
- title: "Install the Security Engine",
- desc: "Runs on your server, detects attack patterns in real time. Immediately protected, and continuously updated with CrowdSec Community Blocklist.",
- },
- {
- num: 2,
- icon: "🛡️",
- hint: "RECOMMENDED",
- title: "Activate the WAF module",
- desc: "Layer in the AppSec component to inspect HTTP traffic and block web exploits before they reach your app.",
- },
- {
- num: 3,
- icon: "📋",
- hint: "OPTIONAL",
- title: "Subscribe to blocklists",
- desc: "Add a selection of extra blocklists on top of the built-in detection & community blocklist",
- },
- {
- num: 4,
- icon: "✍️",
- hint: "OPTIONAL",
- title: "Craft your own rules",
- desc: "Write custom scenarios for your stack, then share them back with the community on the Hub.",
- },
- ],
+ icon: ,
+ label: "Activate the WAF",
+ href: "/docs/next/appsec/intro",
+ color: CS_TEAL,
},
{
- id: "schema-blocklists",
- color: GREEN,
- eyebrowIcon: "🚫",
- eyebrow: "Blocklists",
- title: "Push curated threat feeds directly into your firewall, CDN, or WAF",
- ctaLabel: "Discover Blocklists →",
- ctaHref: "/blocklists",
- steps: [
- {
- num: 1,
- icon: "🔌",
- title: "Create a blocklist integration endpoint",
- desc: "Generates a dedicated URL and credentials to serve blocklists to your perimeter devices.",
- },
- {
- num: 2,
- icon: "🗂️",
- title: "Choose which blocklists to serve",
- desc: "Select from curated feeds by threat category: scanners, bots, TOR exits, exploits, and more.",
- },
- {
- num: 3,
- icon: "🔗",
- title: "Plug it in as an external threat feed",
- desc: "Point your firewall, CDN, or WAF at the endpoint. Use the feed to protect your infrastructure.",
- },
- ],
+ icon: ,
+ label: "Measure what is being blocked",
+ href: "/u/console/remediation_metrics",
+ color: CS_VIOLET,
},
{
- id: "schema-cti",
- color: BLUE,
- eyebrowIcon: "🔍",
- eyebrow: "IP Reputation & CTI",
- title: "Query threat intel in the browser or via API in your tools",
- ctaLabel: "Explore CTI →",
- ctaHref: "/u/cti_api/intro",
- steps: [
- {
- num: 1,
- icon: "🖥️",
- title: "Look up any IP in the Console",
- desc: "Search instantly from our Web UI. Get reputation score, behaviors, attack history, and CVE links.",
- },
- {
- num: 2,
- icon: "🔑",
- hint: "Integrate",
- title: "Generate a CTI API key",
- desc: "Unlock programmatic access to 30+ data points on IPs detected by CrowdSec Network.",
- },
- {
- num: 3,
- icon: "⚙️",
- hint: "Enrich",
- title: "Connect to your SIEM/SOAR/TIP",
- desc: "Native integrations for Splunk, Sentinel, QRadar, TheHive, OpenCTI, MISP, and more.",
- },
- ],
+ icon: ,
+ label: "Check my Stack Health",
+ href: "/u/console/stackhealth",
+ color: CS_BLUE,
},
];
-// ── Page ──────────────────────────────────────────────────────────────────────
+const popularLinks = [
+ {
+ icon: ,
+ label: "Console",
+ href: "/u/console/intro",
+ color: CS_ORANGE,
+ },
+ {
+ icon: ,
+ label: "AppSec / WAF",
+ href: "/docs/next/appsec/intro",
+ color: CS_TEAL,
+ },
+ { label: "CLI Reference", href: "/docs/next/cscli/" },
+ { label: "CTI API Keys", href: "/u/console/ip_reputation/api_keys" },
+ { label: "Troubleshooting", href: "/u/troubleshooting/intro" },
+ { label: "CrowdSec.net", href: "https://www.crowdsec.net", external: true },
+];
-const HomePage = () => {
+export default function HomePage() {
useEffect(() => {
document.body.classList.add("homepage");
document.documentElement.classList.add("homepage");
@@ -488,253 +127,256 @@ const HomePage = () => {
};
}, []);
- const [openSchema, setOpenSchema] = useState(null);
-
- const toggleSchema = (id: string) => setOpenSchema((prev) => (prev === id ? null : id));
-
return (
-
-
- {/* Hero */}
-
+
+
+ {/* ── Hero ── full-width so grid bg spans the viewport ── */}
+
+ {/* Radial glow */}
-
-
- Find the right
-
- CrowdSec tool for you
-
-
- IDPS/WAF | Blocklist feeds | IP Reputation
-
-
-
- {/* Search */}
-
+ {/* Subtle grid */}
+
- {/* Intent strip */}
-
-
+
+ {/* Badge */}
- I want to…
-
-
- {intents.map((i) => (
-
- ))}
+
+ CrowdSec Docs
- {/* Existing user strip */}
-
+ Find the right
+
- Already running CrowdSec?
-
-
- {[
- { label: "🖥️ Open the Console", href: "https://app.crowdsec.net", external: true },
- { label: "🛡️ Activate the WAF", href: "/docs/next/appsec/intro" },
- { label: "📊 Measure what is being Blocked", href: "/u/console/remediation_metrics" },
- { label: "🩺 Check my Stack Health", href: "/u/console/stackhealth" },
- ].map(({ label, href, external }) => (
-
- {label}
- {external && }
-
- ))}
-
-
-
-
+ CrowdSec tool
+ {" "}
+ for you.
+
- {/* How each path works - accordion */}
-
-
-
-
💡 how each path works
-
-
+ Three paths — one platform. Detect attacks at the edge, push curated threat feeds into your stack, or query
+ intel on demand.
+
- {schemas.map((s) => (
-
toggleSchema(s.id)} />
- ))}
+ {/* Search */}
+
+
+
- {/* Not sure / fallback */}
-
-
-
-
-
Not sure where to start?
-
- Answer a few questions and get a recommended path with install steps for your stack.
-
-
-
-
-
- 🧭 Use Case Questionnaire
-
-
-
-
- ⚡ Try in Sandbox
-
-
-
-
+
+ {/* ── "I want to…" eyebrow ── */}
+
+ I want to…
-
- {/* Popular docs */}
-
-
+ {/* ── Path cards ── */}
+
+ }
+ title="Detect & block attacks on servers"
+ desc="Identify and ban bad-behaving IPs from your logs and requests using CrowdSec Detection Scenarios and Virtual-Patching collections."
+ tag="Security Engine"
+ tags={["IDPS", "WAF", "CrowdSec FOSS"]}
+ audience="Sysadmins · DevOps · SRE"
+ href="/security-engine"
+ />
+ }
+ title="Plug CrowdSec blocklists into a firewall, CDN or WAF"
+ desc="Manage network-perimeter devices and want a URL to subscribe to — no agent to install, just curated feeds your equipment can pull."
+ tag="Blocklist Integration Endpoint"
+ tags={["Threat Feeds", "IOC Streams", "Deny-list"]}
+ audience="Network · Platform teams"
+ href="/blocklists"
+ />
+ }
+ title="Investigate IP behaviors and enrich alerts"
+ desc="Security analyst or developer who wants IP context, behaviors, CVEs, aggressivity… in a browser or via REST API."
+ tag="IP Reputation & CTI"
+ tags={["IOC Lookup", "Threat Intel", "CTI API"]}
+ audience="SOC · Threat Intel"
+ href="/u/cti_api/intro"
+ />
+
+
+ {/* ── Already running strip ── */}
+
+
+ {/* ── How each path works ── */}
+
+
- Popular docs
-
-
- {[
- { label: "🖥️ Console", href: "/u/console/intro" },
- { label: "🛡️ AppSec / WAF", href: "/docs/next/appsec/intro" },
- { label: "💻 CLI Reference", href: "/docs/next/cscli/" },
- { label: "🔑 CTI API Keys", href: "/u/console/ip_reputation/api_keys" },
- { label: "❓ Troubleshooting", href: "/u/troubleshooting/intro" },
- // Need to redo the prompt this one is out of date
- // {
- // label: "📖 Docs AI Assistant",
- // href: "https://chatgpt.com/g/g-682c3a61a78081918417571116c2b563-crowdsec-documentation",
- // external: true,
- // },
- { label: "🌐 WWW - CrowdSec", href: "https://www.crowdsec.net", external: true },
- ].map(({ label, href, external }) => (
-
- {label}
- {external && }
-
- ))}
+ How each path works
+
-
+
+
+
+
+
+ {/* ── Guided setup ── */}
+
+
+ {/* ── Popular docs ── */}
+
+
);
-};
-
-export default HomePage;
+}
diff --git a/crowdsec-docs/src/pages/security-engine.tsx b/crowdsec-docs/src/pages/security-engine.tsx
index 76583a020..7547717f7 100644
--- a/crowdsec-docs/src/pages/security-engine.tsx
+++ b/crowdsec-docs/src/pages/security-engine.tsx
@@ -1,6 +1,8 @@
import ForwardIcon from "@mui/icons-material/Forward";
import HubIcon from "@mui/icons-material/Hub";
import MonitorHeartIcon from "@mui/icons-material/MonitorHeart";
+import DocCard from "@site/src/components/docs/DocCard";
+import DocCardGrid from "@site/src/components/docs/DocCardGrid";
import cibApple from "@site/static/img/logo/apple-colored.svg";
import cibDocker from "@site/static/img/logo/docker-colored.svg";
import cibFreebsd from "@site/static/img/logo/freebsd-colored.svg";
@@ -12,7 +14,7 @@ import pfSenseLogo from "@site/static/img/logo-pfsense.svg";
import whmLogo from "@site/static/img/logo-whm.svg";
import React from "react";
import { HomePageItem } from "../components/home-page/home-item";
-import { FeatureCard, FeatureCardProps, ProductPageLayout, Section } from "../components/product-page";
+import { ProductPageLayout, Section } from "../components/product-page";
type PlatformData = {
icon: React.ComponentType>;
@@ -48,60 +50,6 @@ const multiServerSetup: PlatformData[] = [
},
];
-const features: FeatureCardProps[] = [
- {
- title: "Parsers & Scenarios",
- description: "Learn how CrowdSec parses logs and detects threats with community-maintained scenarios.",
- link: "/docs/next/concepts",
- icon: "📊",
- },
- {
- title: "Remediation Components",
- description: "Block threats at firewalls, web servers, and CDNs with remediation components.",
- link: "/u/bouncers/intro",
- icon: "/img/icons/shield-target.webp",
- },
- {
- title: "Console Integration",
- description: "Connect to the CrowdSec Console for centralized management, alerts, and analytics.",
- link: "/u/console/intro",
- icon: "📱",
- },
- {
- title: "AppSec / WAF",
- description: "Protect web applications from OWASP Top 10 risks and custom attack patterns.",
- link: "/docs/next/appsec/intro",
- icon: "/img/icons/waf.webp",
- },
-];
-
-const nextSteps: FeatureCardProps[] = [
- {
- title: "Post-Installation Checklist",
- description: "Essential steps after installing the Security Engine",
- link: "/u/getting_started/next_steps",
- icon: "✅",
- },
- {
- title: "CLI Reference",
- description: "Complete cscli command documentation",
- link: "/docs/next/cscli/",
- icon: "💻",
- },
- {
- title: "Configuration",
- description: "Fine-tune your Security Engine settings",
- link: "/docs/next/configuration/crowdsec_configuration",
- icon: "⚙️",
- },
- {
- title: "Troubleshooting",
- description: "Common issues and how to resolve them",
- link: "/u/troubleshooting/intro",
- icon: "🔧",
- },
-];
-
const SecurityEnginePage = () => {
return (
{
{ label: "Troubleshooting", link: "/u/troubleshooting/intro" },
]}
>
- {/* Installation Section - custom layout for platforms */}
-
-
-
-
Installation
-
- Choose your platform to install the Security Engine. Each guide covers setup, configuration, and enrolling in
- the CrowdSec Console to sync decisions and metrics.
-
-
-
- {/* Single Server */}
-
-
Single Server
-
- Deploy on a single host or service for fast detection and blocking.
-
-
- {singleServerSetup.map((props) => (
-
- ))}
-
+ {/* Installation Section */}
+
+
+
+ Single Server
-
- {/* Healthcheck */}
-
-
+
+ Deploy on a single host or service for fast detection and blocking.
+
+
+ {singleServerSetup.map((p) => (
+
+ ))}
+
-
- *Logos and trademarks are property of their respective owners.
-
+
+
+
+
+ *Logos and trademarks are property of their respective owners.
+
- {/* Multi-Server */}
-
-
Multi-Server
-
- Scale detection across fleets with centralized alerts or logs.
-
-
- {multiServerSetup.map((props) => (
-
- ))}
-
+
+
+ Multi-Server
+
+ Scale detection across fleets with centralized alerts or logs.
+
+
+ {multiServerSetup.map((p) => (
+
+ ))}
+
-
+
-
-
- {features.map((feature) => (
-
- ))}
-
+ {/* Key Capabilities */}
+
+ {/* After Installation */}
-
- {nextSteps.map((step) => (
-
- ))}
-
+
+
+
+
+
+
);
diff --git a/crowdsec-docs/src/theme/Admonition/Layout/index.tsx b/crowdsec-docs/src/theme/Admonition/Layout/index.tsx
index d911a62fb..7d580d96f 100644
--- a/crowdsec-docs/src/theme/Admonition/Layout/index.tsx
+++ b/crowdsec-docs/src/theme/Admonition/Layout/index.tsx
@@ -1,36 +1,48 @@
+import { cilCheckCircle, cilDiamond, cilInfo, cilLightbulb, cilWarning, cilXCircle } from "@coreui/icons";
+import { CIcon } from "@coreui/icons-react";
import { ThemeClassNames } from "@docusaurus/theme-common";
import type { Props } from "@theme/Admonition/Layout";
import clsx from "clsx";
import React, { type ReactNode } from "react";
-import styles from "./styles.module.css";
+const ICON_STYLE = { width: 15, height: 15 };
-function AdmonitionContainer({ type, className, children }: Pick & { children: ReactNode }) {
- return (
-
- {children}
-
- );
-}
+const TYPE_ICONS: Record = {
+ note: ,
+ tip: ,
+ info: ,
+ warning: ,
+ danger: ,
+ premium: ,
+ caution: ,
+ secondary: ,
+ important: ,
+ success: ,
+};
-function AdmonitionHeading({ icon }: Readonly>) {
- return (
-
- {icon}
-
- );
-}
+const TYPE_DEFAULTS: Record = {
+ note: "NOTE",
+ tip: "TIP",
+ info: "INFO",
+ warning: "WARNING",
+ danger: "DANGER",
+ premium: "PREMIUM",
+ caution: "CAUTION",
+};
-function AdmonitionContent({ children }: Pick) {
- return children ? {children}
: null;
-}
+export default function AdmonitionLayout({ type, icon: _icon, title, children, className }: Readonly): React.JSX.Element {
+ const svgIcon = TYPE_ICONS[type] ?? TYPE_ICONS.note;
+ const displayTitle = title ?? TYPE_DEFAULTS[type] ?? type.toUpperCase();
-export default function AdmonitionLayout(props: Readonly): React.JSX.Element {
- const { type, icon, title, children, className } = props;
return (
-
- {title || icon ? : null}
- {children}
-
+
+
+ {svgIcon}
+
+
+
{displayTitle}
+ {children &&
{children}
}
+
+
);
}
diff --git a/crowdsec-docs/src/theme/Admonition/Types.tsx b/crowdsec-docs/src/theme/Admonition/Types.tsx
new file mode 100644
index 000000000..b978eb89a
--- /dev/null
+++ b/crowdsec-docs/src/theme/Admonition/Types.tsx
@@ -0,0 +1,12 @@
+import AdmonitionLayout from "@theme/Admonition/Layout";
+import DefaultAdmonitionTypes from "@theme-original/Admonition/Types";
+import React from "react";
+
+function PremiumAdmonition(props: { title?: string; children?: React.ReactNode }) {
+ return [0])} type="premium" title={props.title ?? "PREMIUM"} />;
+}
+
+export default {
+ ...DefaultAdmonitionTypes,
+ premium: PremiumAdmonition,
+};
diff --git a/crowdsec-docs/src/theme/CodeBlock/Buttons/CopyButton/index.tsx b/crowdsec-docs/src/theme/CodeBlock/Buttons/CopyButton/index.tsx
new file mode 100644
index 000000000..a3fc55754
--- /dev/null
+++ b/crowdsec-docs/src/theme/CodeBlock/Buttons/CopyButton/index.tsx
@@ -0,0 +1,33 @@
+import { useCodeBlockContext } from "@docusaurus/theme-common/internal";
+import { useCallback, useEffect, useRef, useState } from "react";
+
+export default function CopyButton({ className }: Readonly<{ className?: string }>) {
+ const {
+ metadata: { code },
+ } = useCodeBlockContext();
+ const [isCopied, setIsCopied] = useState(false);
+ const timeout = useRef | undefined>(undefined);
+
+ const copyCode = useCallback(() => {
+ if (!navigator?.clipboard) return;
+ navigator.clipboard.writeText(code).then(() => {
+ setIsCopied(true);
+ timeout.current = setTimeout(() => setIsCopied(false), 1500);
+ });
+ }, [code]);
+
+ useEffect(() => () => clearTimeout(timeout.current), []);
+
+ return (
+
+ {isCopied ? "COPIED ✓" : "COPY"}
+
+ );
+}
diff --git a/crowdsec-docs/src/theme/CodeBlock/Content/index.tsx b/crowdsec-docs/src/theme/CodeBlock/Content/index.tsx
new file mode 100644
index 000000000..95823cf6a
--- /dev/null
+++ b/crowdsec-docs/src/theme/CodeBlock/Content/index.tsx
@@ -0,0 +1,76 @@
+import { useCodeBlockContext } from "@docusaurus/theme-common/internal";
+import CopyButton from "@theme/CodeBlock/Buttons/CopyButton";
+import type { Props } from "@theme/CodeBlock/Content";
+import OriginalCodeBlockContent from "@theme-original/CodeBlock/Content";
+import React from "react";
+
+/* Abbreviated language labels for the chrome pill */
+const LANG_LABEL: Record = {
+ bash: "SH",
+ shell: "SH",
+ sh: "SH",
+ zsh: "SH",
+ console: "SH",
+ yaml: "YAML",
+ yml: "YAML",
+ json: "JSON",
+ json5: "JSON",
+ typescript: "TS",
+ tsx: "TSX",
+ javascript: "JS",
+ jsx: "JSX",
+ python: "PY",
+ py: "PY",
+ go: "GO",
+ rust: "RS",
+ ruby: "RB",
+ php: "PHP",
+ diff: "DIFF",
+ text: "TEXT",
+ plaintext: "TEXT",
+ txt: "TEXT",
+ sql: "SQL",
+ dockerfile: "DOCKER",
+ docker: "DOCKER",
+ toml: "TOML",
+ ini: "INI",
+ cfg: "CFG",
+ nginx: "NGINX",
+ apache: "APACHE",
+ css: "CSS",
+ scss: "SCSS",
+ html: "HTML",
+ xml: "XML",
+ markdown: "MD",
+ mdx: "MDX",
+ hcl: "HCL",
+ terraform: "HCL",
+ powershell: "PS1",
+ ps: "PS1",
+ kotlin: "KT",
+ swift: "SWIFT",
+ java: "JAVA",
+};
+
+export default function CodeBlockContentWithChrome(props: Props): React.JSX.Element {
+ const { metadata } = useCodeBlockContext();
+ const lang = (metadata.language ?? "").toLowerCase();
+ const langLabel = LANG_LABEL[lang] ?? (lang ? lang.toUpperCase() : "CODE");
+ const title = metadata.title;
+
+ return (
+
+ {/* ── Chrome bar ── always visible ── */}
+
+ {langLabel}
+ {title && {title} }
+
+
+
+ {/* ── Code area (pre + original buttons hidden via CSS) ── */}
+
+
+
+
+ );
+}
diff --git a/crowdsec-docs/src/theme/DocItem/Content/index.tsx b/crowdsec-docs/src/theme/DocItem/Content/index.tsx
new file mode 100644
index 000000000..d68317620
--- /dev/null
+++ b/crowdsec-docs/src/theme/DocItem/Content/index.tsx
@@ -0,0 +1,16 @@
+import { useDoc } from "@docusaurus/plugin-content-docs/client";
+import type { Props } from "@theme/DocItem/Content";
+import DocItemContent from "@theme-original/DocItem/Content";
+import React from "react";
+
+export default function DocItemContentWrapper(props: Props): React.JSX.Element {
+ const { frontMatter } = useDoc();
+ const eyebrow = (frontMatter as Record).eyebrow as string | undefined;
+
+ return (
+ <>
+ {eyebrow && {eyebrow} }
+
+ >
+ );
+}
diff --git a/crowdsec-docs/src/theme/DocSidebarItem/Category/index.tsx b/crowdsec-docs/src/theme/DocSidebarItem/Category/index.tsx
index 42f6e7de1..5464af13a 100644
--- a/crowdsec-docs/src/theme/DocSidebarItem/Category/index.tsx
+++ b/crowdsec-docs/src/theme/DocSidebarItem/Category/index.tsx
@@ -4,8 +4,6 @@ import { translate } from "@docusaurus/Translate";
import { Collapsible, ThemeClassNames, useCollapsible, usePrevious, useThemeConfig } from "@docusaurus/theme-common";
import { isSamePath } from "@docusaurus/theme-common/internal";
import useIsBrowser from "@docusaurus/useIsBrowser";
-import { Badge } from "@site/src/ui/badge";
-import { Tooltip } from "@site/src/ui/tooltip";
import type { Props } from "@theme/DocSidebarItem/Category";
import DocSidebarItems from "@theme/DocSidebarItems";
import clsx from "clsx";
@@ -196,11 +194,23 @@ export default function DocSidebarItemCategory({
>
{label}
{isPremium && (
-
-
- Premium
-
-
+
+ Premium
+
)}
diff --git a/crowdsec-docs/src/theme/DocSidebarItem/Link/index.tsx b/crowdsec-docs/src/theme/DocSidebarItem/Link/index.tsx
index 34dc5f5b2..d88780349 100644
--- a/crowdsec-docs/src/theme/DocSidebarItem/Link/index.tsx
+++ b/crowdsec-docs/src/theme/DocSidebarItem/Link/index.tsx
@@ -2,12 +2,25 @@ import isInternalUrl from "@docusaurus/isInternalUrl";
import Link from "@docusaurus/Link";
import { isActiveSidebarItem } from "@docusaurus/plugin-content-docs/client";
import { ThemeClassNames } from "@docusaurus/theme-common";
-import { Badge } from "@site/src/ui/badge";
import type { Props } from "@theme/DocSidebarItem/Link";
import clsx from "clsx";
import { ExternalLinkIcon, Signpost } from "lucide-react";
import React from "react";
+const premiumBadge: React.CSSProperties = {
+ fontFamily: "var(--cs-font-mono)",
+ fontSize: 9.5,
+ letterSpacing: "0.08em",
+ textTransform: "uppercase",
+ padding: "2px 6px",
+ borderRadius: 4,
+ background: "color-mix(in srgb, var(--cs-orange) 14%, transparent)",
+ color: "var(--cs-orange)",
+ fontWeight: 600,
+ flexShrink: 0,
+ marginLeft: "auto",
+};
+
export default function DocSidebarItemLink({ item, onItemClick, activePath, level, index, ...props }: Readonly): React.JSX.Element {
const { href, label, className, autoAddBaseUrl, customProps } = item;
const isActive = isActiveSidebarItem(item, activePath);
@@ -31,7 +44,7 @@ export default function DocSidebarItemLink({ item, onItemClick, activePath, leve
{
"menu__link--active": isActive,
},
- tag === "premium" ? "flex items-center justify-between" : "flex items-center"
+ "flex items-center"
)}
autoAddBaseUrl={autoAddBaseUrl}
aria-current={isActive ? "page" : undefined}
@@ -42,11 +55,7 @@ export default function DocSidebarItemLink({ item, onItemClick, activePath, leve
{...props}
>
{label}
- {tag === "premium" && (
-
- Premium
-
- )}
+ {tag === "premium" && Premium }
{tag === "otherSection" && }
{!isInternalLink && }
diff --git a/crowdsec-docs/src/theme/Footer/Layout/index.tsx b/crowdsec-docs/src/theme/Footer/Layout/index.tsx
index e24fa9391..90900401f 100644
--- a/crowdsec-docs/src/theme/Footer/Layout/index.tsx
+++ b/crowdsec-docs/src/theme/Footer/Layout/index.tsx
@@ -1,25 +1,172 @@
import type { Props } from "@theme/Footer/Layout";
import React from "react";
-export default function FooterLayout({ links, logo, copyright }: Readonly): React.JSX.Element {
+export default function FooterLayout({ links, logo: _logo, copyright }: Readonly): React.JSX.Element {
return (
-
-
-
-
-
-
CrowdSec
-
Safer together.
+
);
diff --git a/crowdsec-docs/src/theme/MDXComponents.tsx b/crowdsec-docs/src/theme/MDXComponents.tsx
new file mode 100644
index 000000000..2d26725f9
--- /dev/null
+++ b/crowdsec-docs/src/theme/MDXComponents.tsx
@@ -0,0 +1,44 @@
+import AccessCard from "@site/src/components/docs/AccessCard";
+import ChallengeGrid from "@site/src/components/docs/ChallengeGrid";
+import ConsoleMockup from "@site/src/components/docs/ConsoleMockup";
+import DocCard from "@site/src/components/docs/DocCard";
+import DocCardGrid from "@site/src/components/docs/DocCardGrid";
+import DocsHero from "@site/src/components/docs/DocsHero";
+import GuidedSetupCard from "@site/src/components/docs/GuidedSetupCard";
+import PathCard from "@site/src/components/docs/PathCard";
+import PathCards from "@site/src/components/docs/PathCards";
+import Pill from "@site/src/components/docs/Pill";
+import PopularChips from "@site/src/components/docs/PopularChips";
+import PromoCard from "@site/src/components/docs/PromoCard";
+import QuickStrip from "@site/src/components/docs/QuickStrip";
+import RunningStrip from "@site/src/components/docs/RunningStrip";
+import MDXComponents from "@theme-original/MDXComponents";
+import React from "react";
+
+/* Thin table wrapper — adds .cs-table-wrap border-radius treatment */
+function TableWrapper({ children, ...props }: React.TableHTMLAttributes
) {
+ return (
+
+ );
+}
+
+export default {
+ ...MDXComponents,
+ table: TableWrapper,
+ AccessCard,
+ ChallengeGrid,
+ ConsoleMockup,
+ DocCard,
+ DocCardGrid,
+ DocsHero,
+ GuidedSetupCard,
+ PathCard,
+ PathCards,
+ Pill,
+ PopularChips,
+ PromoCard,
+ QuickStrip,
+ RunningStrip,
+};
diff --git a/crowdsec-docs/src/theme/TOC/index.tsx b/crowdsec-docs/src/theme/TOC/index.tsx
index 88ed93ad3..a00d75204 100644
--- a/crowdsec-docs/src/theme/TOC/index.tsx
+++ b/crowdsec-docs/src/theme/TOC/index.tsx
@@ -1,23 +1,35 @@
-import ConsoleAd from "@site/src/components/console-ad";
+import { useLocation } from "@docusaurus/router";
+import PromoCard, { type PromoVariant } from "@site/src/components/docs/PromoCard";
import TOCItems from "@theme/TOCItems";
import clsx from "clsx";
import React from "react";
-// Define custom classNames
const LINK_CLASS_NAME = "table-of-contents__link toc-highlight";
const LINK_ACTIVE_CLASS_NAME = "table-of-contents__link--active";
-// Modified Table of Contents component
+function usePromoVariant(): PromoVariant {
+ const { pathname } = useLocation();
+ if (pathname.startsWith("/u/cti_api") || pathname.startsWith("/u/console/ip_reputation")) return "cti";
+ if (pathname.startsWith("/u/blocklists") || pathname.startsWith("/blocklists")) return "engine";
+ if (pathname.startsWith("/u/console")) return "console";
+ return "console";
+}
+
+function RailPromoCard() {
+ const variant = usePromoVariant();
+ return ;
+}
+
const TableOfContent = ({ className, ...props }): React.JSX.Element => (
-
+
);
diff --git a/crowdsec-docs/src/utils/colorMix.ts b/crowdsec-docs/src/utils/colorMix.ts
new file mode 100644
index 000000000..09bbf9550
--- /dev/null
+++ b/crowdsec-docs/src/utils/colorMix.ts
@@ -0,0 +1,2 @@
+/** Returns a `color-mix(in srgb, …)` expression for tinting with transparency. */
+export const mix = (color: string, pct: number) => `color-mix(in srgb, ${color} ${pct}%, transparent)`;
diff --git a/crowdsec-docs/tailwind.config.js b/crowdsec-docs/tailwind.config.js
index e1de857a0..bd0480d00 100644
--- a/crowdsec-docs/tailwind.config.js
+++ b/crowdsec-docs/tailwind.config.js
@@ -6,12 +6,30 @@ module.exports = {
darkMode: ["class", '[data-theme="dark"]'],
content: [
"./src/**/*.{js,jsx,ts,tsx,mdx}",
+ "./plugins/**/*.{js,jsx,ts,tsx}",
"./unversioned/**/*.{js,jsx,ts,tsx,mdx}",
"./versioned_docs/**/*.{js,jsx,ts,tsx,mdx}",
],
theme: {
extend: {
colors: {
+ "cs-bg": "var(--cs-bg)",
+ "cs-bg-soft": "var(--cs-bg-soft)",
+ "cs-surface": "var(--cs-surface)",
+ "cs-surface-2": "var(--cs-surface-2)",
+ "cs-border": "var(--cs-border)",
+ "cs-border-hi": "var(--cs-border-hi)",
+ "cs-ink": "var(--cs-ink)",
+ "cs-ink-dim": "var(--cs-ink-dim)",
+ "cs-ink-mute": "var(--cs-ink-mute)",
+ "cs-orange": "var(--cs-orange)",
+ "cs-orange-soft": "var(--cs-orange-soft)",
+ "cs-teal": "var(--cs-teal)",
+ "cs-violet": "var(--cs-violet)",
+ "cs-blue": "var(--cs-blue)",
+ "cs-pink": "var(--cs-pink)",
+ "cs-red": "var(--cs-red)",
+ "cs-btn-text": "var(--cs-btn-text)",
primary: `rgb(var(--primary) / )`,
"primary-foreground": `rgb(var(--primary-foreground) / )`,
secondary: `rgb(var(--secondary) / )`,
@@ -45,6 +63,10 @@ module.exports = {
backgroundImage: () => ({
landing: "url('/img/landing-page-bg.webp')",
}),
+ fontFamily: {
+ "cs-sans": ["var(--cs-font-sans)"],
+ "cs-mono": ["var(--cs-font-mono)"],
+ },
scale: {
99: "0.99",
101: "1.01",
diff --git a/crowdsec-docs/unversioned/blocklists/getting_started.mdx b/crowdsec-docs/unversioned/blocklists/getting_started.mdx
index 419c9751e..aa8e25b06 100644
--- a/crowdsec-docs/unversioned/blocklists/getting_started.mdx
+++ b/crowdsec-docs/unversioned/blocklists/getting_started.mdx
@@ -1,31 +1,88 @@
---
id: getting_started
title: Getting Started
+eyebrow: Blocklists · 02
---
-import ConsolePromo from '@site/src/components/console-promo';
+import { CIcon } from "@coreui/icons-react";
+import { cilCheckCircle } from "@coreui/icons";
+import DocCard from '@site/src/components/docs/DocCard';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
-Make sure to read the [Introduction](/blocklists/intro.md) to understand CrowdSec Blocklists, how they work, and their available tiers.
+New to CrowdSec blocklists? Read the [Introduction](/u/blocklists/intro) first to understand how they work and the available tiers. Then choose one of the two paths below — most people pick **one or the other, not both**.
-When you're ready, you can get started with Blocklists in two ways:
+## Choose your path
-1. **Security Engine** - Use the CrowdSec Security Engine to ingest blocklists
-2. **Integrations** - Use Integrations to ingest blocklists into firewall, CDN, or other security solutions
+
+
+
+ {[
+ "Behavior + reputation in one engine",
+ "60+ bouncers (NGINX, FW, CDN…)",
+ "Free community blocklists included",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
-Depending on which path you take you can start with the following guides:
+
+
+ {[
+ "Cloudflare, AWS WAF, Fortinet…",
+ "Setup in minutes from the Console",
+ "Premium tiers for richer feeds",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
-
-
-
-
-If you're new to CrowdSec, and want to use blocklists we recommend starting with the [Integrations guide](integrations/intro.mdx), however, if you are unsure where to start, feel free to browse our [main website for more information](https://www.crowdsec.net/).
+## Quick comparison
+
+export const pill = (color) => ({
+ display: "inline-flex", alignItems: "center",
+ fontFamily: "var(--cs-font-mono)", fontSize: 10.5, letterSpacing: "0.1em",
+ textTransform: "uppercase", fontWeight: 600,
+ padding: "2px 10px", borderRadius: 999,
+ background: `color-mix(in srgb, ${color} 14%, transparent)`,
+ color,
+});
+
+
+
+
+
+ Security Engine
+ Integrations
+
+
+
+ Setup time ~ 10 min ~ 2 min
+ Runs on Your infrastructure Your existing firewall / CDN
+ Behavior detection Yes — full engine No — blocklists only
+ Best for Servers, gateways, k8s Edge, WAF, managed CDN
+ Free community feed Included Included
+
+
diff --git a/crowdsec-docs/unversioned/console/intro.mdx b/crowdsec-docs/unversioned/console/intro.mdx
index db08e2477..ff266e5d0 100644
--- a/crowdsec-docs/unversioned/console/intro.mdx
+++ b/crowdsec-docs/unversioned/console/intro.mdx
@@ -1,25 +1,43 @@
---
id: intro
title: Introduction to the CrowdSec Console
+eyebrow: Console · Introduction
+rightRailPromo: console
---
+import AccessCard from '@site/src/components/docs/AccessCard';
+import ConsoleMockup from '@site/src/components/docs/ConsoleMockup';
+import DocCard from '@site/src/components/docs/DocCard';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
+
CrowdSec is an open-source and collaborative cybersecurity solution that protects websites, servers, and IT infrastructures from attacks. It leverages the power of the community to create a continuously updated security model that adapts to emerging threats. By analyzing users' logs in real-time, CrowdSec identifies and blocks malicious behavior across the entire network. The participatory approach ensures that the intelligence gathered is shared across the community when one user is attacked, enabling all users to block potential threats without external intervention.
## The CrowdSec Console
The CrowdSec Console is a central component of the CrowdSec ecosystem, offering a user-friendly web interface that allows administrators to visualize and manage the security data generated by CrowdSec. The Console enhances the overall usability of CrowdSec by providing a clear, intuitive platform for monitoring threats and adjusting security policies on IT infrastructures.
+
+
## A tool to mitigate security challenges
The CrowdSec Console addresses several key cybersecurity challenges:
-* **Complexity of Threat Detection**: By providing a unified interface for monitoring and analysis, it simplifies the process of identifying and understanding security threats.
-* **Response Time**: Enhances the ability to quickly respond to and mitigate detected threats, reducing potential damage.
-* **Scalability and Management**: This offers a scalable solution that can manage security data across multiple installations, ideal for small and large businesses.
-* **Proactive Threat Blocking**: The Console enables users to leverage dynamically updated blocklists, which are crucial for preemptively blocking known malicious IP addresses before they can attack. These blocklists are generated through the collective intelligence of the CrowdSec community, offering a robust layer of defense against widespread threats.
-* **Informed Decision-Making**: Access to detailed CTI data enables administrators to make informed decisions about their security posture. It helps in understanding adversaries' tactics, techniques, and procedures (TTPs), thus improving the effectiveness of response strategies and security policies.
+
+
+
+
+
+
+
+
## Accessing the Console
-Getting access to the Console is as simple as following [this link](https://app.crowdsec.net/signup?mtm_campaign=Console&mtm_source=docs&mtm_medium=pageAd&mtm_content=Introduction) and creating your account.
+Getting access to the Console is straightforward:
+
diff --git a/crowdsec-docs/unversioned/console/ip_reputation/intro.mdx b/crowdsec-docs/unversioned/console/ip_reputation/intro.mdx
index 5a7075591..c70b38d42 100644
--- a/crowdsec-docs/unversioned/console/ip_reputation/intro.mdx
+++ b/crowdsec-docs/unversioned/console/ip_reputation/intro.mdx
@@ -6,50 +6,42 @@ description: Explore and query CrowdSec's IP Reputation data and manage CTI API
import Link from "@docusaurus/Link";
import { ExternalLink } from "lucide-react";
+import DocCard from '@site/src/components/docs/DocCard';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
-export const BLUE = "#60a5fa";
-export const GREEN = "#22d3a0";
-export const PURPLE = "#a78bfa";
+export const BLUE = "var(--cs-blue)";
+export const GREEN = "var(--cs-teal)";
+export const PURPLE = "var(--cs-violet)";
Query behavioral intelligence on any IP: reputation scores, attack patterns, linked CVEs, and activity history. Sourced from hundreds of thousands of real CrowdSec deployments worldwide.
-{/* ── Row 1: two cards side by side ──────────────────────────────────── */}
-
-
-
- {/* Card 1: Web UI exploration (merged Search + IP Report) */}
-
-
🔍
-
Explore in the Web UI
-
- No setup needed. Search any IP directly from your browser: run Lucene queries with live faceted filters (reputation, country, AS, behaviors, classifications) and open any result to see its full report: threat score , behaviors mapped to MITRE ATT&CK, linked CVEs , and time-windowed activity. The homepage also surfaces a Top 10 Most Aggressive IPs leaderboard updated every 24h.
-
-
- IP Search →
- Advanced Search →
- IP Report →
- Lucene Query Reference →
-
-
-
- {/* Card 2: Enrich your Alerts (API Key) */}
-
-
🔑
-
Enrich your Alerts
-
- Unlock programmatic access to 30+ enrichment fields per IP: reputation, behaviors, CVEs, attack context, MITRE mappings, and more. Use it to enrich SIEM alerts, automate lookups, or feed threat intel platforms. Free tier included, no credit card needed.
-
-
-
- Create an API key →
- Data Taxonomy →
- API Reference
-
-
-
-
+
+
+
+
{/* ── You might also be interested in: LET ───────────────────────────── */}
diff --git a/crowdsec-docs/unversioned/console/premium_upgrade/features_overview.mdx b/crowdsec-docs/unversioned/console/premium_upgrade/features_overview.mdx
index cb4fd9e46..2d2408bb2 100644
--- a/crowdsec-docs/unversioned/console/premium_upgrade/features_overview.mdx
+++ b/crowdsec-docs/unversioned/console/premium_upgrade/features_overview.mdx
@@ -6,6 +6,7 @@ toc_max_heading_level: 2
---
import { FeatureCard, HighlightCard } from '@site/src/components/premium-upgrade/feature-card';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
Premium features enable multiple use cases.
Make the best use of the premium features for your needs in: **Scaling, Multi-tenancy, Enhanced proactive protection, Centralized management, Team collaboration, Integration and automation, Enhanced threat intelligence, and improved support.**
@@ -14,7 +15,7 @@ Make the best use of the premium features for your needs in: **Scaling, Multi-te
## Scaling, Automation & Multi-Tenancy
-
+
-
+
---
## Extra Protection
-
+
-
+
---
## Reactivity & Monitoring
-
+
-
+
diff --git a/crowdsec-docs/unversioned/console/premium_upgrade/testing_premium.mdx b/crowdsec-docs/unversioned/console/premium_upgrade/testing_premium.mdx
index a6ba3a2a4..31fe2c227 100644
--- a/crowdsec-docs/unversioned/console/premium_upgrade/testing_premium.mdx
+++ b/crowdsec-docs/unversioned/console/premium_upgrade/testing_premium.mdx
@@ -6,6 +6,9 @@ toc_max_heading_level: 2
---
import { FeatureCard, HighlightCard } from '@site/src/components/premium-upgrade/feature-card';
+import DocCard from '@site/src/components/docs/DocCard';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
+import QuickStrip from '@site/src/components/docs/QuickStrip';
@@ -33,45 +36,11 @@ Premium protection features are automatically enabled when you upgrade:
-
-
-
-
-### 📊 Metric 1: Remediation Ratio
-
-**How to measure:**
-Check your Console dashboard for proactive vs reactive blocking ratio.
-
-**Expected result:**
-2× more proactive blocking (blocklist hits vs real-time decisions)
-
-
-
-
-
-### 💻 Metric 2: Server Resources
-
-**How to measure:**
-Monitor CPU, memory, and bandwidth usage on your Security Engines before and after.
-
-**Expected result:**
-75-92% reduction in malicious traffic reaching your servers
-
-
-
-
-
-### 📝 Metric 3: Log Volume
-
-**How to measure:**
-Check your SIEM or log aggregator for alert volume changes.
-
-**Expected result:**
-Cleaner logs, reduced alert fatigue, fewer false positives
-
-
-
-
+
+
+
+
+
-
+
-
+
@@ -164,7 +133,7 @@ Test multi-tenant and automation capabilities:
-
+
-
+
@@ -215,67 +184,24 @@ Test multi-tenant and automation capabilities:
## 🎓 Recommended Trial Timeline
-
-
-
-
-### Week 1: Protection
-
-- Enable all blocklists
-- Activate Background Noise
-- Turn on Remediation Sync
-- Measure baseline metrics
-
-
-
-
-
-### Week 2: Team
-
-- Invite team members
-- Test CTI lookups
-- Configure push notifications
-- Analyze historical trends
-
-
-
-
-
-### Week 3: Scale
-
-- Create test organizations
-- Test SAPI endpoints
-- Try Auto Enroll
-- Custom blocklist sharing
-
-
-
-
-
-### Week 4: Review
-
-- Compare metrics vs Week 1
-- Document value realized
-- Plan production rollout
-- Prepare upgrade decision
-
-
-
-
+
+
+
+
+
+
---
## 💡 Need Help Testing?
-
-
-### Questions about your trial?
-
Our team can help you set up proper testing and measure the value in your specific environment.
-
-
-
+
diff --git a/crowdsec-docs/unversioned/cti_api/intro.mdx b/crowdsec-docs/unversioned/cti_api/intro.mdx
index c3cff06b7..98a4e6cd3 100644
--- a/crowdsec-docs/unversioned/cti_api/intro.mdx
+++ b/crowdsec-docs/unversioned/cti_api/intro.mdx
@@ -7,11 +7,14 @@ sidebar_position: 1
import Link from "@docusaurus/Link";
import { ExternalLink } from "lucide-react";
import CtiIntegrationTile, { ctiIntegrations } from '@site/src/components/cti-integration-tile';
+import DocCard from '@site/src/components/docs/DocCard';
+import DocCardGrid from '@site/src/components/docs/DocCardGrid';
+import QuickStrip from '@site/src/components/docs/QuickStrip';
-export const BLUE = "#60a5fa";
-export const ORANGE = "#f97316";
-export const PURPLE = "#a78bfa";
-export const GREEN = "#22d3a0";
+export const BLUE = "var(--cs-blue)";
+export const ORANGE = "var(--cs-orange)";
+export const PURPLE = "var(--cs-violet)";
+export const GREEN = "var(--cs-teal)";
{/* ── Hero ─────────────────────────────────────────────────────────────── */}
@@ -23,55 +26,55 @@ export const GREEN = "#22d3a0";
Every lookup gives you
behavioral context : what the IP was doing, where, and when.
-{/* Quick access strip - same pattern as "Already running CrowdSec?" on the homepage */}
-
-
Quick access
-
- 🔍 Look up an IP
- 🔑 Get an API key
- 🎯 Hunt for threats
-
-
+
{/* ── How do you want to use it? ──────────────────────────────────────── */}
Entry points
How do you want to use it?
-
- {[
- {
- badge: "🔍 No setup needed", icon: "🖥️", accent: BLUE,
- title: "Web UI investigation - in the Console",
- desc: "Search any IP instantly. Explore threat history and the top aggressive IPs in the last 24h. No API key needed.",
- links: [{ label: "Web UI guide →", href: "/u/console/ip_reputation/intro" }, { label: "IP Report →", href: "/u/console/ip_reputation/ip_report" }],
- },
- {
- badge: "⚙️ Developer / SecOps", icon: "🔌", accent: ORANGE,
- title: "Enrich Alerts via API",
- desc: "Use the CTI API to add CrowdSec IP context to SIEM alerts, SOAR workflows, TIPs, scripts, and internal tools.",
- links: [{ label: "API quickstart →", href: "/u/cti_api/api_introduction" }, { label: "All integrations →", href: "/u/cti_api/api_integration/integration_intro" }],
- },
- {
- badge: "🎯 Threat hunter", icon: "🚨", accent: PURPLE,
- title: "Hunt active threats",
- desc: "Advanced Search with live faceted filters (behavior, country, AS, CVE) to find campaigns or build blocklists.",
- links: [{ label: "Advanced search →", href: "/u/console/ip_reputation/search_ui_advanced" }, { label: "Live Exploit Tracker →", href: "/u/tracker_api/intro" }],
- },
- ].map(({ badge, icon, accent, title, desc, links }) => (
-
-
{badge}
-
{icon}
-
{title}
-
{desc}
-
- {links.map(({ label, href }) => (
- {label}
- ))}
-
-
- ))}
-
+
+
+
+
+
{/* ── Why CrowdSec CTI (informational, de-emphasized) ────────────────── */}
@@ -136,35 +139,21 @@ export const GREEN = "#22d3a0";
Technical details
-
- {[
- { icon: "📊", title: "Data Taxonomy", desc: "CTI Data structure, scores, behaviors and classifications", href: "/u/cti_api/taxonomy/intro" },
- { icon: "📚", title: "API Reference", desc: "Full endpoint reference with request/response schemas.", href: "https://crowdsecurity.github.io/cti-api/", external: true },
- { icon: "❓", title: "FAQ", desc: "Common questions about access, quotas, and data.", href: "/u/cti_api/faq" },
- ].map(({ icon, title, desc, href, external }) => (
-
-
{icon}
-
-
- {title}{external && }
-
-
{desc}
-
-
- ))}
-
+
+
+
+
+
{/* ── Need help ───────────────────────────────────────────────────────── */}
-
-
-
Need help?
-
Get answers in Discord or check the FAQ.
-
-
- 💬 Join Discord
- ❓ View FAQ
- 📚 API Reference
-
-
+