Mkdocs search fixers
This commit is contained in:
parent
e2a1ac0113
commit
533783bcae
@ -9,6 +9,10 @@
|
||||
"CML_SERVICE_EMAIL": "admin@bnkops.ca",
|
||||
"CML_SERVICE_PASSWORD": "ChangeMe2025!"
|
||||
}
|
||||
},
|
||||
"playwright": {
|
||||
"command": "npx",
|
||||
"args": ["@playwright/mcp@latest", "--headless"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ import type { ProductInsertResult } from '@/components/payments/ProductInsertMod
|
||||
import { AdPickerModal } from '@/components/media/AdPickerModal';
|
||||
import type { AdInsertResult } from '@/components/media/AdPickerModal';
|
||||
import { PollInsertModal } from '@/components/scheduling/PollInsertModal';
|
||||
import { WikiLinkPickerModal } from '@/components/docs/WikiLinkPickerModal';
|
||||
import { useDocsCollaboration } from '@/hooks/useDocsCollaboration';
|
||||
import { CollaboratorAvatars } from '@/components/docs/CollaboratorAvatars';
|
||||
import { YTextareaBinding } from '@/lib/y-textarea';
|
||||
@ -257,6 +258,7 @@ export function MobileDocsEditor({ editor, collabEnabled = false }: MobileDocsEd
|
||||
const [productInsertOpen, setProductInsertOpen] = useState(false);
|
||||
const [adPickerOpen, setAdPickerOpen] = useState(false);
|
||||
const [pollInsertOpen, setPollInsertOpen] = useState(false);
|
||||
const [wikiLinkPickerOpen, setWikiLinkPickerOpen] = useState(false);
|
||||
|
||||
const {
|
||||
fileTree,
|
||||
@ -450,6 +452,7 @@ export function MobileDocsEditor({ editor, collabEnabled = false }: MobileDocsEd
|
||||
case 'product-card': setProductInsertOpen(true); break;
|
||||
case 'ad-insert': setAdPickerOpen(true); break;
|
||||
case 'scheduling-poll': setPollInsertOpen(true); break;
|
||||
case 'wiki-link': setWikiLinkPickerOpen(true); break;
|
||||
case 'pricing-table': {
|
||||
const appUrl = config
|
||||
? `${window.location.protocol}//${config.domain.replace(/^([^.]+)/, 'app')}`
|
||||
@ -885,6 +888,28 @@ export function MobileDocsEditor({ editor, collabEnabled = false }: MobileDocsEd
|
||||
<ProductInsertModal open={productInsertOpen} onClose={() => setProductInsertOpen(false)} onInsert={handleProductInsert} />
|
||||
<AdPickerModal open={adPickerOpen} onCancel={() => setAdPickerOpen(false)} onInsert={handleAdInsert} />
|
||||
<PollInsertModal open={pollInsertOpen} onCancel={() => setPollInsertOpen(false)} onInsert={handlePollInsert} />
|
||||
<WikiLinkPickerModal
|
||||
open={wikiLinkPickerOpen}
|
||||
fileTree={fileTree}
|
||||
onClose={() => setWikiLinkPickerOpen(false)}
|
||||
onSelect={(wikiLink) => {
|
||||
const ta = textareaRef.current;
|
||||
if (ta) {
|
||||
const { selectionStart, value } = ta;
|
||||
const before = value.substring(0, selectionStart);
|
||||
const after = value.substring(selectionStart);
|
||||
onContentChange(before + wikiLink + after);
|
||||
requestAnimationFrame(() => {
|
||||
ta.focus();
|
||||
const pos = selectionStart + wikiLink.length;
|
||||
ta.setSelectionRange(pos, pos);
|
||||
});
|
||||
} else {
|
||||
onContentChange(fileContent + wikiLink);
|
||||
}
|
||||
setWikiLinkPickerOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
type TextareaInsertResult,
|
||||
} from '@/utils/textareaSnippets';
|
||||
|
||||
export type InsertRequestType = 'video-card' | 'photo-insert' | 'donate-button' | 'pricing-table' | 'product-card' | 'ad-insert' | 'scheduling-poll';
|
||||
export type InsertRequestType = 'video-card' | 'photo-insert' | 'donate-button' | 'pricing-table' | 'product-card' | 'ad-insert' | 'scheduling-poll' | 'wiki-link';
|
||||
|
||||
interface MobileFormattingToolbarProps {
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
@ -70,6 +70,8 @@ const MORE_SNIPPETS: SnippetDef[] = [
|
||||
{ id: 'footnote', label: 'Footnote', group: 'Insert', run: (ta) => insertBlock(ta, '[^1]\n\n[^1]: Text') },
|
||||
{ id: 'def-list', label: 'Definition List', group: 'Insert', run: (ta) => insertBlock(ta, 'Term\n: Definition') },
|
||||
{ id: 'hr', label: 'Horizontal Rule', group: 'Insert', run: (ta) => insertBlock(ta, '---') },
|
||||
// Links
|
||||
{ id: 'wiki-link', label: 'Wiki Link [[]]', group: 'Links', insertType: 'wiki-link' },
|
||||
// Insert — modal-based (open picker)
|
||||
{ id: 'video-card', label: 'Video Card', group: 'Media & Widgets', insertType: 'video-card' },
|
||||
{ id: 'photo-insert', label: 'Photo', group: 'Media & Widgets', insertType: 'photo-insert' },
|
||||
|
||||
153
admin/src/components/docs/WikiLinkPickerModal.tsx
Normal file
153
admin/src/components/docs/WikiLinkPickerModal.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { Modal, Input, List, theme, Typography, Tag } from 'antd';
|
||||
import { FileOutlined, PictureOutlined } from '@ant-design/icons';
|
||||
import type { FileNode } from '@/types/api';
|
||||
|
||||
interface FlatFile {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']);
|
||||
|
||||
function isImage(name: string): boolean {
|
||||
const dot = name.lastIndexOf('.');
|
||||
return dot >= 0 && IMAGE_EXTENSIONS.has(name.substring(dot).toLowerCase());
|
||||
}
|
||||
|
||||
function flattenFiles(nodes: FileNode[]): FlatFile[] {
|
||||
const out: FlatFile[] = [];
|
||||
for (const n of nodes) {
|
||||
if (n.isDirectory) {
|
||||
if (n.children) out.push(...flattenFiles(n.children));
|
||||
} else {
|
||||
out.push({ path: n.path, name: n.name });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
interface WikiLinkPickerModalProps {
|
||||
open: boolean;
|
||||
fileTree: FileNode[];
|
||||
onSelect: (wikiLink: string) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function WikiLinkPickerModal({ open, fileTree, onSelect, onClose }: WikiLinkPickerModalProps) {
|
||||
const { token } = theme.useToken();
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const allFiles = useMemo(() => flattenFiles(fileTree), [fileTree]);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
if (!search.trim()) return allFiles;
|
||||
const q = search.toLowerCase();
|
||||
return allFiles.filter(
|
||||
f => f.name.toLowerCase().includes(q) || f.path.toLowerCase().includes(q)
|
||||
);
|
||||
}, [allFiles, search]);
|
||||
|
||||
// Group: images first if searching for images, docs first otherwise
|
||||
const docs = useMemo(() => filtered.filter(f => !isImage(f.name)), [filtered]);
|
||||
const images = useMemo(() => filtered.filter(f => isImage(f.name)), [filtered]);
|
||||
|
||||
const handleSelect = (file: FlatFile) => {
|
||||
const img = isImage(file.name);
|
||||
const linkName = file.name.endsWith('.md') ? file.name.slice(0, -3) : file.name;
|
||||
// For images, use ![[name]] syntax; for docs, use [[name]]
|
||||
const wikiLink = img ? `![[${linkName}]]` : `[[${linkName}]]`;
|
||||
onSelect(wikiLink);
|
||||
setSearch('');
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Insert Wiki Link"
|
||||
open={open}
|
||||
onCancel={() => { onClose(); setSearch(''); }}
|
||||
footer={null}
|
||||
destroyOnHidden
|
||||
width={420}
|
||||
>
|
||||
<Input.Search
|
||||
placeholder="Search files..."
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
allowClear
|
||||
autoFocus
|
||||
style={{ marginBottom: 12 }}
|
||||
/>
|
||||
|
||||
<div style={{ maxHeight: 360, overflow: 'auto' }}>
|
||||
{docs.length > 0 && (
|
||||
<>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, padding: '4px 0', display: 'block' }}>
|
||||
Documents
|
||||
</Typography.Text>
|
||||
<List
|
||||
size="small"
|
||||
dataSource={docs.slice(0, 30)}
|
||||
renderItem={item => (
|
||||
<List.Item
|
||||
style={{ padding: '8px 8px', cursor: 'pointer' }}
|
||||
onClick={() => handleSelect(item)}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, width: '100%' }}>
|
||||
<FileOutlined style={{ color: token.colorTextSecondary, flexShrink: 0 }} />
|
||||
<div style={{ minWidth: 0, flex: 1 }}>
|
||||
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{item.name.replace(/\.md$/, '')}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: token.colorTextTertiary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{item.path}
|
||||
</div>
|
||||
</div>
|
||||
<Tag color="blue" style={{ marginInlineEnd: 0 }}>doc</Tag>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{images.length > 0 && (
|
||||
<>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, padding: '4px 0', display: 'block' }}>
|
||||
Images
|
||||
</Typography.Text>
|
||||
<List
|
||||
size="small"
|
||||
dataSource={images.slice(0, 20)}
|
||||
renderItem={item => (
|
||||
<List.Item
|
||||
style={{ padding: '8px 8px', cursor: 'pointer' }}
|
||||
onClick={() => handleSelect(item)}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, width: '100%' }}>
|
||||
<PictureOutlined style={{ color: token.colorTextSecondary, flexShrink: 0 }} />
|
||||
<div style={{ minWidth: 0, flex: 1 }}>
|
||||
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{item.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: token.colorTextTertiary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{item.path}
|
||||
</div>
|
||||
</div>
|
||||
<Tag color="green" style={{ marginInlineEnd: 0 }}>img</Tag>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{docs.length === 0 && images.length === 0 && (
|
||||
<div style={{ textAlign: 'center', padding: 24, color: token.colorTextTertiary }}>
|
||||
No files found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@ -124,7 +124,7 @@ export function buildFeatureFlags(settings: Record<string, any> | null | undefin
|
||||
}
|
||||
|
||||
/** Check whether a single feature flag passes */
|
||||
function flagPasses(flagName: string, flags: Record<string, boolean | undefined>): boolean {
|
||||
export function flagPasses(flagName: string, flags: Record<string, boolean | undefined>): boolean {
|
||||
if (OPT_OUT_FLAGS.has(flagName)) {
|
||||
return flags[flagName] !== false;
|
||||
}
|
||||
|
||||
@ -89,6 +89,8 @@ import { useDocsCollaboration } from '@/hooks/useDocsCollaboration';
|
||||
import { CollaboratorAvatars } from '@/components/docs/CollaboratorAvatars';
|
||||
import { MonacoBinding } from 'y-monaco';
|
||||
import type { SiteSettings } from '@/types/api';
|
||||
import { registerWikiLinkCompletion } from '@/utils/wikiLinkCompletion';
|
||||
import { WikiLinkPickerModal } from '@/components/docs/WikiLinkPickerModal';
|
||||
|
||||
type LayoutMode = 'split' | 'editor' | 'preview';
|
||||
type PreviewMode = 'desktop' | 'mobile';
|
||||
@ -368,6 +370,7 @@ const SNIPPETS: MkDocsSnippet[] = [
|
||||
{ id: 'product-card', label: 'Product Card', group: 'insert', type: 'insert', template: '' },
|
||||
{ id: 'ad-insert', label: 'Ad', group: 'insert', type: 'insert', template: '' },
|
||||
{ id: 'scheduling-poll', label: 'Scheduling Poll', group: 'insert', type: 'insert', template: '' },
|
||||
{ id: 'wiki-link', label: 'Wiki Link [[]]', group: 'insert', type: 'insert', template: '' },
|
||||
{ id: 'hr', label: 'Horizontal Rule', group: 'insert', type: 'insert', template: '---' },
|
||||
];
|
||||
|
||||
@ -612,6 +615,7 @@ export default function DocsPage() {
|
||||
const [productInsertOpen, setProductInsertOpen] = useState(false);
|
||||
const [adPickerOpen, setAdPickerOpen] = useState(false);
|
||||
const [pollInsertOpen, setPollInsertOpen] = useState(false);
|
||||
const [wikiLinkPickerOpen, setWikiLinkPickerOpen] = useState(false);
|
||||
const [dragOver, setDragOver] = useState(false);
|
||||
const dragCounter = useRef(0);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
@ -623,6 +627,7 @@ export default function DocsPage() {
|
||||
const monacoEditorRef = useRef<monacoEditor.IStandaloneCodeEditor | null>(null);
|
||||
const monacoRef = useRef<typeof import('monaco-editor') | null>(null);
|
||||
const monacoBindingRef = useRef<MonacoBinding | null>(null);
|
||||
const fileTreeRef = useRef<FileNode[]>([]);
|
||||
const [editorReady, setEditorReady] = useState(false);
|
||||
|
||||
// --- Collaboration state ---
|
||||
@ -640,6 +645,9 @@ export default function DocsPage() {
|
||||
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
|
||||
// Keep fileTreeRef in sync for Monaco autocomplete callback
|
||||
useEffect(() => { fileTreeRef.current = fileTree; }, [fileTree]);
|
||||
|
||||
// Fetch file tree
|
||||
const fetchTree = useCallback(async (showLoading = true, force = false) => {
|
||||
try {
|
||||
@ -819,6 +827,9 @@ export default function DocsPage() {
|
||||
});
|
||||
});
|
||||
|
||||
// Register wiki-link [[ autocomplete
|
||||
registerWikiLinkCompletion(monaco, () => fileTreeRef.current);
|
||||
|
||||
// Custom right-click context menu (replaces Monaco's flat list)
|
||||
const domNode = ed.getDomNode();
|
||||
if (domNode) {
|
||||
@ -912,6 +923,12 @@ export default function DocsPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wiki link — opens file picker or inserts [[ for autocomplete
|
||||
if (snippetId === 'wiki-link') {
|
||||
setWikiLinkPickerOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pricing table — static CTA (plans are dynamic, so link out)
|
||||
if (snippetId === 'pricing-table') {
|
||||
const appUrl = config
|
||||
@ -2091,7 +2108,7 @@ export default function DocsPage() {
|
||||
<Dropdown menu={{ items: SNIPPETS.filter(s => s.group === 'insert').map(s => ({
|
||||
key: s.id,
|
||||
label: s.label,
|
||||
icon: s.id === 'video-card' ? <PlayCircleOutlined /> : s.id === 'photo-insert' ? <PictureOutlined /> : s.id === 'donate-button' ? <HeartOutlined /> : s.id === 'pricing-table' ? <CrownOutlined /> : s.id === 'product-card' ? <ShoppingCartOutlined /> : s.id === 'ad-insert' ? <BuildOutlined /> : s.id === 'scheduling-poll' ? <CalendarOutlined /> : s.id === 'link' ? <LinkOutlined /> : s.id === 'image' ? <FileMarkdownOutlined /> : s.id === 'table' ? <TableOutlined /> : <PlusOutlined />,
|
||||
icon: s.id === 'wiki-link' ? <NodeExpandOutlined /> : s.id === 'video-card' ? <PlayCircleOutlined /> : s.id === 'photo-insert' ? <PictureOutlined /> : s.id === 'donate-button' ? <HeartOutlined /> : s.id === 'pricing-table' ? <CrownOutlined /> : s.id === 'product-card' ? <ShoppingCartOutlined /> : s.id === 'ad-insert' ? <BuildOutlined /> : s.id === 'scheduling-poll' ? <CalendarOutlined /> : s.id === 'link' ? <LinkOutlined /> : s.id === 'image' ? <FileMarkdownOutlined /> : s.id === 'table' ? <TableOutlined /> : <PlusOutlined />,
|
||||
onClick: () => handleToolbarSnippet(s.id),
|
||||
})) }} trigger={['click']}>
|
||||
<Button type="text" size="small" style={{ height: 24, fontSize: 12 }}>
|
||||
@ -2311,6 +2328,23 @@ export default function DocsPage() {
|
||||
onInsert={handlePollInsert}
|
||||
/>
|
||||
|
||||
<WikiLinkPickerModal
|
||||
open={wikiLinkPickerOpen}
|
||||
fileTree={fileTree}
|
||||
onClose={() => setWikiLinkPickerOpen(false)}
|
||||
onSelect={(wikiLink) => {
|
||||
const ed = monacoEditorRef.current;
|
||||
if (ed) {
|
||||
const sel = ed.getSelection();
|
||||
if (sel) {
|
||||
ed.executeEdits('wiki-link-insert', [{ range: sel, text: wikiLink }]);
|
||||
ed.focus();
|
||||
}
|
||||
}
|
||||
setWikiLinkPickerOpen(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Custom right-click context menu with submenus */}
|
||||
{ctxMenu && (
|
||||
<div
|
||||
|
||||
@ -808,7 +808,7 @@ export default function MkDocsSettingsPage() {
|
||||
label: 'Settings',
|
||||
children: (
|
||||
<div style={{ maxWidth: 900 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16, gap: 8 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
@ -818,6 +818,16 @@ export default function MkDocsSettingsPage() {
|
||||
>
|
||||
Save Settings
|
||||
</Button>
|
||||
<Tooltip title="Build static site">
|
||||
<Button
|
||||
icon={building ? <LoadingOutlined /> : <BuildOutlined />}
|
||||
onClick={triggerBuild}
|
||||
loading={building}
|
||||
disabled={!isSuperAdmin}
|
||||
>
|
||||
Build
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Card title="Site Info" size="small" style={{ marginBottom: 16 }}>
|
||||
<Form
|
||||
@ -1151,7 +1161,7 @@ export default function MkDocsSettingsPage() {
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', padding: '16px 0' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', padding: '16px 0', gap: 8 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
@ -1161,6 +1171,16 @@ export default function MkDocsSettingsPage() {
|
||||
>
|
||||
Save Navigation
|
||||
</Button>
|
||||
<Tooltip title="Build static site">
|
||||
<Button
|
||||
icon={building ? <LoadingOutlined /> : <BuildOutlined />}
|
||||
onClick={triggerBuild}
|
||||
loading={building}
|
||||
disabled={!isSuperAdmin}
|
||||
>
|
||||
Build
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* Nav Add/Edit Modal */}
|
||||
@ -1223,15 +1243,27 @@ export default function MkDocsSettingsPage() {
|
||||
<Space>
|
||||
{editorDirty && <Text type="warning">Modified</Text>}
|
||||
</Space>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={saveEditor}
|
||||
loading={editorSaving}
|
||||
disabled={!editorDirty || !isSuperAdmin}
|
||||
>
|
||||
Save (Ctrl+S)
|
||||
</Button>
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={saveEditor}
|
||||
loading={editorSaving}
|
||||
disabled={!editorDirty || !isSuperAdmin}
|
||||
>
|
||||
Save (Ctrl+S)
|
||||
</Button>
|
||||
<Tooltip title="Build static site">
|
||||
<Button
|
||||
icon={building ? <LoadingOutlined /> : <BuildOutlined />}
|
||||
onClick={triggerBuild}
|
||||
loading={building}
|
||||
disabled={!isSuperAdmin}
|
||||
>
|
||||
Build
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</div>
|
||||
<div style={{ flex: 1, minHeight: 0, border: `1px solid ${token.colorBorderSecondary}`, borderRadius: 4 }}>
|
||||
<Editor
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate, useOutletContext } from 'react-router-dom';
|
||||
import {
|
||||
Input,
|
||||
Switch,
|
||||
@ -24,6 +24,7 @@ import {
|
||||
PlusOutlined,
|
||||
FolderOutlined,
|
||||
FolderAddOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import type { AppOutletContext } from '@/components/AppLayout';
|
||||
@ -32,6 +33,8 @@ import {
|
||||
DEFAULT_NAV_ITEMS,
|
||||
ICON_MAP,
|
||||
mergeNavDefaults,
|
||||
buildFeatureFlags,
|
||||
flagPasses,
|
||||
} from '@/lib/nav-defaults';
|
||||
|
||||
export default function NavigationSettingsPage() {
|
||||
@ -45,6 +48,22 @@ export default function NavigationSettingsPage() {
|
||||
const [customLinkLabel, setCustomLinkLabel] = useState('');
|
||||
const [customLinkPath, setCustomLinkPath] = useState('');
|
||||
|
||||
const navigate = useNavigate();
|
||||
const featureFlags = useMemo(() => buildFeatureFlags(settings), [settings]);
|
||||
|
||||
const isFeatureFlagDisabled = (item: NavItem): boolean => {
|
||||
if (!item.featureFlag) return false;
|
||||
return !flagPasses(item.featureFlag, featureFlags);
|
||||
};
|
||||
|
||||
// Check if any item has a disabled feature flag (for warning banner)
|
||||
const hasDisabledFlags = useMemo(() => {
|
||||
return navItems.some(item => {
|
||||
if (isFeatureFlagDisabled(item)) return true;
|
||||
return item.children?.some(c => isFeatureFlagDisabled(c)) ?? false;
|
||||
});
|
||||
}, [navItems, featureFlags]);
|
||||
|
||||
useEffect(() => {
|
||||
setPageHeader({ title: 'Navigation' });
|
||||
return () => setPageHeader(null);
|
||||
@ -254,11 +273,13 @@ export default function NavigationSettingsPage() {
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderItemRow = (item: NavItem, _idx: number, siblings: NavItem[], indent: boolean) => {
|
||||
const renderItemRow = (item: NavItem, _idx: number, siblings: NavItem[], indent: boolean, parentFlagDisabled = false) => {
|
||||
const isGroup = item.type === 'group';
|
||||
const sorted = [...siblings].sort((a, b) => a.order - b.order);
|
||||
const sortedIdx = sorted.findIndex(i => i.id === item.id);
|
||||
const parentGroupId = indent ? findParentGroupId(item.id) : null;
|
||||
const ownFlagDisabled = isFeatureFlagDisabled(item);
|
||||
const flagDisabled = ownFlagDisabled || parentFlagDisabled;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -279,8 +300,10 @@ export default function NavigationSettingsPage() {
|
||||
border: isGroup
|
||||
? '1px solid rgba(100,150,255,0.15)'
|
||||
: '1px solid rgba(255,255,255,0.08)',
|
||||
borderLeft: indent ? '3px solid rgba(100,150,255,0.3)' : undefined,
|
||||
opacity: item.enabled ? 1 : 0.5,
|
||||
borderLeft: flagDisabled
|
||||
? '3px dashed rgba(250,173,20,0.6)'
|
||||
: indent ? '3px solid rgba(100,150,255,0.3)' : undefined,
|
||||
opacity: item.enabled ? (flagDisabled ? 0.55 : 1) : 0.5,
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
@ -362,21 +385,46 @@ export default function NavigationSettingsPage() {
|
||||
) : (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{item.featureFlag ? (
|
||||
<Tooltip title={`Controlled by ${item.featureFlag}`}>
|
||||
<Tag color="cyan" style={{ margin: 0, fontSize: 10 }}>
|
||||
{item.featureFlag.replace('enable', '')}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
ownFlagDisabled ? (
|
||||
<Tooltip title={<span>Feature disabled in Settings. <a onClick={() => navigate('/app/settings', { state: { tab: 'features' } })} style={{ color: '#69b1ff', cursor: 'pointer' }}>Feature Toggles</a></span>}>
|
||||
<Tag color="orange" style={{ margin: 0, fontSize: 10, cursor: 'help' }}>
|
||||
<ExclamationCircleOutlined style={{ marginRight: 3 }} />
|
||||
{item.featureFlag.replace('enable', '')} Off
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={`Controlled by ${item.featureFlag}`}>
|
||||
<Tag color="cyan" style={{ margin: 0, fontSize: 10 }}>
|
||||
{item.featureFlag.replace('enable', '')}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)
|
||||
) : (
|
||||
<Tag color="geekblue" style={{ margin: 0, fontSize: 10 }}>group</Tag>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isGroup && (
|
||||
<div style={{ display: 'none' }}>
|
||||
{/* Placeholder — tag column handled by the Select above */}
|
||||
{/* Feature flag tag for non-group items */}
|
||||
{!isGroup && item.featureFlag ? (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{ownFlagDisabled ? (
|
||||
<Tooltip title={<span>Feature disabled in Settings. <a onClick={() => navigate('/app/settings', { state: { tab: 'features' } })} style={{ color: '#69b1ff', cursor: 'pointer' }}>Feature Toggles</a></span>}>
|
||||
<Tag color="orange" style={{ margin: 0, fontSize: 10, cursor: 'help' }}>
|
||||
<ExclamationCircleOutlined style={{ marginRight: 3 }} />
|
||||
{item.featureFlag.replace('enable', '')} Off
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={`Controlled by ${item.featureFlag}`}>
|
||||
<Tag color="cyan" style={{ margin: 0, fontSize: 10 }}>
|
||||
{item.featureFlag.replace('enable', '')}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
) : !isGroup ? (
|
||||
<div />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -389,8 +437,25 @@ export default function NavigationSettingsPage() {
|
||||
type="info"
|
||||
message="Configure the navigation bar shown on all public pages, the admin header, Gancio events page, and MkDocs site. Groups appear as dropdowns on desktop and collapsible sections on mobile."
|
||||
showIcon
|
||||
style={{ marginBottom: 24 }}
|
||||
style={{ marginBottom: hasDisabledFlags ? 12 : 24 }}
|
||||
/>
|
||||
{hasDisabledFlags && (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
message={
|
||||
<span>
|
||||
Some nav items have their feature disabled in Settings.
|
||||
Items with <Tag color="orange" style={{ margin: '0 4px', fontSize: 10 }}><ExclamationCircleOutlined style={{ marginRight: 3 }} />Off</Tag>
|
||||
won't appear in public navigation until the feature is enabled.{' '}
|
||||
<Button type="link" size="small" style={{ padding: 0 }} onClick={() => navigate('/app/settings', { state: { tab: 'features' } })}>
|
||||
Feature Toggles
|
||||
</Button>
|
||||
</span>
|
||||
}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 16 }}>
|
||||
{sorted.map((item, idx) => (
|
||||
@ -398,7 +463,7 @@ export default function NavigationSettingsPage() {
|
||||
{renderItemRow(item, idx, sorted, false)}
|
||||
{/* Render children indented below their group */}
|
||||
{item.type === 'group' && item.children && [...item.children].sort((a, b) => a.order - b.order).map((child, childIdx) =>
|
||||
renderItemRow(child, childIdx, item.children!, true)
|
||||
renderItemRow(child, childIdx, item.children!, true, isFeatureFlagDisabled(item))
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
117
admin/src/utils/wikiLinkCompletion.ts
Normal file
117
admin/src/utils/wikiLinkCompletion.ts
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Monaco Editor wiki-link autocomplete provider.
|
||||
*
|
||||
* Registers a completion provider for markdown that triggers on `[`
|
||||
* and provides file suggestions when the user types `[[`.
|
||||
*/
|
||||
|
||||
import type { FileNode } from '@/types/api';
|
||||
|
||||
interface FlatFile {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']);
|
||||
|
||||
function isImage(name: string): boolean {
|
||||
const dot = name.lastIndexOf('.');
|
||||
return dot >= 0 && IMAGE_EXTENSIONS.has(name.substring(dot).toLowerCase());
|
||||
}
|
||||
|
||||
function flattenFiles(nodes: FileNode[]): FlatFile[] {
|
||||
const out: FlatFile[] = [];
|
||||
for (const n of nodes) {
|
||||
if (n.isDirectory) {
|
||||
if (n.children) out.push(...flattenFiles(n.children));
|
||||
} else {
|
||||
out.push({ path: n.path, name: n.name });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a wiki-link `[[` autocomplete provider for Monaco's markdown mode.
|
||||
*
|
||||
* @param monaco - The Monaco namespace (from onMount callback)
|
||||
* @param getFileTree - Callback returning the current file tree
|
||||
* @returns IDisposable to unregister the provider
|
||||
*/
|
||||
export function registerWikiLinkCompletion(
|
||||
monaco: typeof import('monaco-editor'),
|
||||
getFileTree: () => FileNode[],
|
||||
): import('monaco-editor').IDisposable {
|
||||
return monaco.languages.registerCompletionItemProvider('markdown', {
|
||||
triggerCharacters: ['['],
|
||||
provideCompletionItems(model, position) {
|
||||
// Check that we're in a `[[` context
|
||||
const lineContent = model.getLineContent(position.lineNumber);
|
||||
const textBefore = lineContent.substring(0, position.column - 1);
|
||||
|
||||
// Must end with `[[` (possibly with partial text after it)
|
||||
const wikiStart = textBefore.lastIndexOf('[[');
|
||||
if (wikiStart < 0) return { suggestions: [] };
|
||||
|
||||
// Make sure there's no `]]` between wikiStart and cursor (not already closed)
|
||||
const between = textBefore.substring(wikiStart + 2);
|
||||
if (between.includes(']]')) return { suggestions: [] };
|
||||
|
||||
// Check if this is an image embed (![[)
|
||||
const isEmbed = wikiStart > 0 && textBefore[wikiStart - 1] === '!';
|
||||
|
||||
// The partial query the user has typed after `[[`
|
||||
const query = between.toLowerCase();
|
||||
|
||||
// Build the range from after `[[` to current cursor (or past any auto-closed `]]`)
|
||||
const startCol = wikiStart + 3; // +2 for `[[`, +1 for 1-based
|
||||
const textAfter = lineContent.substring(position.column - 1);
|
||||
// If Monaco auto-closed brackets, there may be `]]` right after cursor — consume it
|
||||
const closingMatch = textAfter.match(/^\]{1,2}/);
|
||||
const extraClose = closingMatch ? closingMatch[0].length : 0;
|
||||
const range = new monaco.Range(
|
||||
position.lineNumber,
|
||||
startCol,
|
||||
position.lineNumber,
|
||||
position.column + extraClose,
|
||||
);
|
||||
|
||||
const files = flattenFiles(getFileTree());
|
||||
const suggestions: import('monaco-editor').languages.CompletionItem[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const fileIsImage = isImage(file.name);
|
||||
|
||||
// If user typed `![[`, prefer images; if `[[`, prefer docs
|
||||
// But show all files regardless — just sort differently
|
||||
const displayName = file.name.endsWith('.md')
|
||||
? file.name.slice(0, -3)
|
||||
: file.name;
|
||||
|
||||
// Filter by query
|
||||
if (query && !displayName.toLowerCase().includes(query) && !file.path.toLowerCase().includes(query)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For wiki-links, we insert just the name (hook resolves the path)
|
||||
const insertName = file.name.endsWith('.md')
|
||||
? file.name.slice(0, -3)
|
||||
: file.name;
|
||||
|
||||
suggestions.push({
|
||||
label: displayName,
|
||||
kind: fileIsImage
|
||||
? monaco.languages.CompletionItemKind.File
|
||||
: monaco.languages.CompletionItemKind.Reference,
|
||||
detail: file.path,
|
||||
insertText: insertName + ']]',
|
||||
range,
|
||||
// Sort: prioritize images for ![[, docs for [[
|
||||
sortText: (isEmbed ? (fileIsImage ? '0' : '1') : (fileIsImage ? '1' : '0')) + displayName,
|
||||
});
|
||||
}
|
||||
|
||||
return { suggestions };
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -7,12 +7,24 @@ if [ "$NODE_ENV" = "production" ] && [ "$NODE_TLS_REJECT_UNAUTHORIZED" = "0" ];
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for PostgreSQL to be ready before running migrations
|
||||
echo "Waiting for database..."
|
||||
MAX_WAIT=30
|
||||
WAITED=0
|
||||
until echo "SELECT 1" | npx prisma db execute --stdin --schema ./prisma/schema.prisma 2>/dev/null; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
if [ $WAITED -ge $MAX_WAIT ]; then
|
||||
echo "FATAL: Database not available after ${MAX_WAIT}s"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "Database ready (${WAITED}s)"
|
||||
|
||||
# Run migrations — fail hard on error (never fall back to db push, which causes drift)
|
||||
echo "Running Prisma migrations..."
|
||||
npx prisma migrate deploy 2>&1 || {
|
||||
echo "Migration failed, falling back to schema push..."
|
||||
npx prisma db push --skip-generate 2>&1
|
||||
}
|
||||
echo "Database sync complete."
|
||||
npx prisma migrate deploy 2>&1
|
||||
echo "Migrations complete."
|
||||
|
||||
echo "Running database seed..."
|
||||
npx prisma db seed 2>&1
|
||||
|
||||
@ -589,6 +589,12 @@ class HeaderBuilderService {
|
||||
<div class="cm-header-nav__links">
|
||||
<div class="cm-header-nav__links-inner">
|
||||
${desktopLinks}
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -621,6 +627,20 @@ class HeaderBuilderService {
|
||||
</div>
|
||||
<div class="cm-header-nav__mobile-links">
|
||||
${mobileLinks}
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -724,6 +744,74 @@ class HeaderBuilderService {
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// This avoids reliance on the ~ sibling combinator (fragile with template blocks).
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -966,8 +1054,127 @@ class HeaderBuilderService {
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
{% if config.theme.palette %}
|
||||
{% if not config.theme.palette is mapping %}
|
||||
{% include "partials/palette.html" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if "material/search" in config.plugins %}
|
||||
{% include "partials/search.html" %}
|
||||
{% endif %}
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{% block tabs %}{% endblock %}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@ -148,6 +148,7 @@ services:
|
||||
- MEDIA_ROOT=/media/local
|
||||
- MEDIA_UPLOADS=/media/uploads
|
||||
- MAX_UPLOAD_SIZE_GB=${MAX_UPLOAD_SIZE_GB:-10}
|
||||
- INITIAL_ADMIN_PASSWORD=${INITIAL_ADMIN_PASSWORD}
|
||||
volumes:
|
||||
- ./api:/app
|
||||
- /app/node_modules
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
"stars_count": 0,
|
||||
"forks_count": 0,
|
||||
"open_issues_count": 23,
|
||||
"updated_at": "2026-03-08T18:11:30-06:00",
|
||||
"updated_at": "2026-03-09T12:23:17-06:00",
|
||||
"created_at": "2025-05-28T14:54:59-06:00",
|
||||
"clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git",
|
||||
"ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T18:11:30-06:00"
|
||||
"last_build_update": "2026-03-09T12:23:17-06:00"
|
||||
}
|
||||
@ -4,10 +4,10 @@
|
||||
"description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.",
|
||||
"html_url": "https://github.com/anthropics/claude-code",
|
||||
"language": "Shell",
|
||||
"stars_count": 75344,
|
||||
"forks_count": 6074,
|
||||
"open_issues_count": 5793,
|
||||
"updated_at": "2026-03-09T00:15:16Z",
|
||||
"stars_count": 75798,
|
||||
"forks_count": 6114,
|
||||
"open_issues_count": 5868,
|
||||
"updated_at": "2026-03-09T21:59:41Z",
|
||||
"created_at": "2025-02-22T17:41:21Z",
|
||||
"clone_url": "https://github.com/anthropics/claude-code.git",
|
||||
"ssh_url": "git@github.com:anthropics/claude-code.git",
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "VS Code in the browser",
|
||||
"html_url": "https://github.com/coder/code-server",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 76540,
|
||||
"forks_count": 6539,
|
||||
"stars_count": 76554,
|
||||
"forks_count": 6541,
|
||||
"open_issues_count": 169,
|
||||
"updated_at": "2026-03-08T21:32:19Z",
|
||||
"updated_at": "2026-03-09T20:21:15Z",
|
||||
"created_at": "2019-02-27T16:50:41Z",
|
||||
"clone_url": "https://github.com/coder/code-server.git",
|
||||
"ssh_url": "git@github.com:coder/code-server.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-06T12:59:10Z"
|
||||
"last_build_update": "2026-03-09T19:30:51Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.",
|
||||
"html_url": "https://github.com/gethomepage/homepage",
|
||||
"language": "JavaScript",
|
||||
"stars_count": 28793,
|
||||
"forks_count": 1811,
|
||||
"stars_count": 28818,
|
||||
"forks_count": 1812,
|
||||
"open_issues_count": 1,
|
||||
"updated_at": "2026-03-08T23:44:14Z",
|
||||
"updated_at": "2026-03-09T21:38:58Z",
|
||||
"created_at": "2022-08-24T07:29:42Z",
|
||||
"clone_url": "https://github.com/gethomepage/homepage.git",
|
||||
"ssh_url": "git@github.com:gethomepage/homepage.git",
|
||||
"default_branch": "dev",
|
||||
"last_build_update": "2026-03-08T12:16:51Z"
|
||||
"last_build_update": "2026-03-09T17:02:06Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD",
|
||||
"html_url": "https://github.com/go-gitea/gitea",
|
||||
"language": "Go",
|
||||
"stars_count": 54180,
|
||||
"forks_count": 6438,
|
||||
"open_issues_count": 2846,
|
||||
"updated_at": "2026-03-08T23:25:42Z",
|
||||
"stars_count": 54194,
|
||||
"forks_count": 6448,
|
||||
"open_issues_count": 2850,
|
||||
"updated_at": "2026-03-09T21:55:30Z",
|
||||
"created_at": "2016-11-01T02:13:26Z",
|
||||
"clone_url": "https://github.com/go-gitea/gitea.git",
|
||||
"ssh_url": "git@github.com:go-gitea/gitea.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T20:49:59Z"
|
||||
"last_build_update": "2026-03-09T10:12:24Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.",
|
||||
"html_url": "https://github.com/knadh/listmonk",
|
||||
"language": "Go",
|
||||
"stars_count": 19221,
|
||||
"stars_count": 19236,
|
||||
"forks_count": 1945,
|
||||
"open_issues_count": 99,
|
||||
"updated_at": "2026-03-08T23:08:55Z",
|
||||
"updated_at": "2026-03-09T21:14:22Z",
|
||||
"created_at": "2019-06-26T05:08:39Z",
|
||||
"clone_url": "https://github.com/knadh/listmonk.git",
|
||||
"ssh_url": "git@github.com:knadh/listmonk.git",
|
||||
"default_branch": "master",
|
||||
"last_build_update": "2026-03-08T14:04:45Z"
|
||||
"last_build_update": "2026-03-09T03:05:44Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Create & scan cute qr codes easily \ud83d\udc7e",
|
||||
"html_url": "https://github.com/lyqht/mini-qr",
|
||||
"language": "Vue",
|
||||
"stars_count": 1898,
|
||||
"forks_count": 240,
|
||||
"open_issues_count": 21,
|
||||
"updated_at": "2026-03-08T15:02:09Z",
|
||||
"stars_count": 1897,
|
||||
"forks_count": 241,
|
||||
"open_issues_count": 22,
|
||||
"updated_at": "2026-03-09T15:15:51Z",
|
||||
"created_at": "2023-04-21T14:20:14Z",
|
||||
"clone_url": "https://github.com/lyqht/mini-qr.git",
|
||||
"ssh_url": "git@github.com:lyqht/mini-qr.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-05T13:18:42Z"
|
||||
"last_build_update": "2026-03-09T07:31:47Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.",
|
||||
"html_url": "https://github.com/n8n-io/n8n",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 178152,
|
||||
"forks_count": 55558,
|
||||
"open_issues_count": 1405,
|
||||
"updated_at": "2026-03-09T00:14:18Z",
|
||||
"stars_count": 178343,
|
||||
"forks_count": 55592,
|
||||
"open_issues_count": 1414,
|
||||
"updated_at": "2026-03-09T21:52:13Z",
|
||||
"created_at": "2019-06-22T09:24:21Z",
|
||||
"clone_url": "https://github.com/n8n-io/n8n.git",
|
||||
"ssh_url": "git@github.com:n8n-io/n8n.git",
|
||||
"default_branch": "master",
|
||||
"last_build_update": "2026-03-09T00:10:51Z"
|
||||
"last_build_update": "2026-03-09T21:33:49Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 A Free & Self-hostable Airtable Alternative",
|
||||
"html_url": "https://github.com/nocodb/nocodb",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 62384,
|
||||
"forks_count": 4655,
|
||||
"open_issues_count": 627,
|
||||
"updated_at": "2026-03-08T23:36:57Z",
|
||||
"stars_count": 62431,
|
||||
"forks_count": 4659,
|
||||
"open_issues_count": 629,
|
||||
"updated_at": "2026-03-09T19:42:51Z",
|
||||
"created_at": "2017-10-29T18:51:48Z",
|
||||
"clone_url": "https://github.com/nocodb/nocodb.git",
|
||||
"ssh_url": "git@github.com:nocodb/nocodb.git",
|
||||
"default_branch": "develop",
|
||||
"last_build_update": "2026-03-07T10:48:26Z"
|
||||
"last_build_update": "2026-03-09T12:25:14Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Get up and running with Kimi-K2.5, GLM-5, MiniMax, DeepSeek, gpt-oss, Qwen, Gemma and other models.",
|
||||
"html_url": "https://github.com/ollama/ollama",
|
||||
"language": "Go",
|
||||
"stars_count": 164479,
|
||||
"forks_count": 14834,
|
||||
"open_issues_count": 2613,
|
||||
"updated_at": "2026-03-09T00:13:40Z",
|
||||
"stars_count": 164642,
|
||||
"forks_count": 14860,
|
||||
"open_issues_count": 2609,
|
||||
"updated_at": "2026-03-09T21:37:49Z",
|
||||
"created_at": "2023-06-26T19:39:32Z",
|
||||
"clone_url": "https://github.com/ollama/ollama.git",
|
||||
"ssh_url": "git@github.com:ollama/ollama.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T06:32:28Z"
|
||||
"last_build_update": "2026-03-09T21:19:34Z"
|
||||
}
|
||||
@ -4,10 +4,10 @@
|
||||
"description": "Documentation that simply works",
|
||||
"html_url": "https://github.com/squidfunk/mkdocs-material",
|
||||
"language": "Python",
|
||||
"stars_count": 26208,
|
||||
"forks_count": 4048,
|
||||
"stars_count": 26215,
|
||||
"forks_count": 4051,
|
||||
"open_issues_count": 2,
|
||||
"updated_at": "2026-03-08T17:43:54Z",
|
||||
"updated_at": "2026-03-09T19:46:16Z",
|
||||
"created_at": "2016-01-28T22:09:23Z",
|
||||
"clone_url": "https://github.com/squidfunk/mkdocs-material.git",
|
||||
"ssh_url": "git@github.com:squidfunk/mkdocs-material.git",
|
||||
|
||||
BIN
mkdocs/docs/hooks/__pycache__/wikilinks_hook.cpython-311.pyc
Normal file
BIN
mkdocs/docs/hooks/__pycache__/wikilinks_hook.cpython-311.pyc
Normal file
Binary file not shown.
BIN
mkdocs/docs/hooks/__pycache__/wikilinks_hook.cpython-313.pyc
Normal file
BIN
mkdocs/docs/hooks/__pycache__/wikilinks_hook.cpython-313.pyc
Normal file
Binary file not shown.
199
mkdocs/docs/hooks/wikilinks_hook.py
Normal file
199
mkdocs/docs/hooks/wikilinks_hook.py
Normal file
@ -0,0 +1,199 @@
|
||||
"""
|
||||
MkDocs Hook for Wiki-Link Resolution
|
||||
|
||||
Converts Obsidian-style [[wiki-links]] to standard Markdown links/images.
|
||||
|
||||
Supported syntax:
|
||||
[[page-name]] → [page-name](../page-name/)
|
||||
[[page-name|Display Text]] → [Display Text](../page-name/)
|
||||
[[page-name#heading]] → [page-name](../page-name/#heading)
|
||||
![[image.png]] → 
|
||||
![[image.png|alt text]] → 
|
||||
|
||||
Links inside fenced code blocks and inline code are skipped.
|
||||
Unresolved links are left as-is with a warning logged.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Module-level file index: filename (lowercase) → relative path from docs root
|
||||
_file_index: Dict[str, str] = {}
|
||||
|
||||
# Whether directory URLs are enabled
|
||||
_use_directory_urls: bool = True
|
||||
|
||||
# Image extensions
|
||||
_IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp', '.tiff', '.avif'}
|
||||
|
||||
|
||||
def on_files(files: Any, config: Dict[str, Any]) -> None:
|
||||
"""Build a file index mapping lowercased filenames to their paths."""
|
||||
global _file_index, _use_directory_urls
|
||||
_file_index.clear()
|
||||
_use_directory_urls = config.get('use_directory_urls', True)
|
||||
|
||||
for f in files:
|
||||
# f.src_path is relative to docs_dir, e.g. "docs/getting-started/installation.md"
|
||||
src = f.src_path
|
||||
basename = os.path.basename(src)
|
||||
# For .md files, also index without extension
|
||||
name_lower = basename.lower()
|
||||
_file_index[name_lower] = src
|
||||
|
||||
if name_lower.endswith('.md'):
|
||||
stem = name_lower[:-3]
|
||||
# Don't overwrite if stem already exists (first wins)
|
||||
if stem not in _file_index:
|
||||
_file_index[stem] = src
|
||||
|
||||
logger.info(f"[wikilinks] Indexed {len(_file_index)} files for wiki-link resolution")
|
||||
|
||||
|
||||
def _resolve_link(name: str) -> Optional[str]:
|
||||
"""Look up a filename in the index (case-insensitive). Returns src_path or None."""
|
||||
key = name.lower().strip()
|
||||
return _file_index.get(key)
|
||||
|
||||
|
||||
def _compute_relative_path(from_page_src: str, to_src: str, anchor: str = '') -> str:
|
||||
"""Compute relative URL from one page to another, accounting for directory URLs."""
|
||||
from_dir = os.path.dirname(from_page_src)
|
||||
|
||||
_, ext = os.path.splitext(to_src)
|
||||
is_image = ext.lower() in _IMAGE_EXTENSIONS
|
||||
is_md = ext.lower() == '.md'
|
||||
|
||||
if is_image:
|
||||
# Images: direct relative path to the file
|
||||
rel = os.path.relpath(to_src, from_dir)
|
||||
return rel.replace(os.sep, '/')
|
||||
|
||||
if is_md and _use_directory_urls:
|
||||
# With directory URLs, pages become page-name/index.html
|
||||
# So we link to the directory (without .md)
|
||||
page_dir = to_src[:-3] # strip .md
|
||||
# Handle index.md → links to the directory itself
|
||||
if os.path.basename(to_src).lower() == 'index.md':
|
||||
page_dir = os.path.dirname(to_src)
|
||||
rel = os.path.relpath(page_dir, from_dir)
|
||||
url = rel.replace(os.sep, '/') + '/'
|
||||
elif is_md:
|
||||
# Without directory URLs, link directly to .md (MkDocs converts to .html)
|
||||
rel = os.path.relpath(to_src, from_dir)
|
||||
url = rel.replace(os.sep, '/').replace('.md', '.html')
|
||||
else:
|
||||
# Other files: direct path
|
||||
rel = os.path.relpath(to_src, from_dir)
|
||||
url = rel.replace(os.sep, '/')
|
||||
|
||||
if anchor:
|
||||
url += '#' + anchor
|
||||
|
||||
return url
|
||||
|
||||
|
||||
# Regex to match wiki-links: optional ! prefix, then [[content]]
|
||||
_WIKILINK_RE = re.compile(r'(!?)\[\[([^\]]+)\]\]')
|
||||
|
||||
|
||||
def _replace_wikilinks(markdown: str, page_src: str) -> str:
|
||||
"""Replace wiki-links in markdown, skipping code blocks."""
|
||||
lines = markdown.split('\n')
|
||||
result_lines = []
|
||||
in_fenced_block = False
|
||||
|
||||
for line in lines:
|
||||
# Track fenced code blocks (``` or ~~~)
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith('```') or stripped.startswith('~~~'):
|
||||
in_fenced_block = not in_fenced_block
|
||||
result_lines.append(line)
|
||||
continue
|
||||
|
||||
if in_fenced_block:
|
||||
result_lines.append(line)
|
||||
continue
|
||||
|
||||
# Process wiki-links in this line, but skip inline code
|
||||
# Strategy: split by inline code spans, only process non-code parts
|
||||
parts = re.split(r'(`[^`]+`)', line)
|
||||
processed_parts = []
|
||||
for part in parts:
|
||||
if part.startswith('`') and part.endswith('`'):
|
||||
# Inside inline code — leave as-is
|
||||
processed_parts.append(part)
|
||||
else:
|
||||
# Process wiki-links in this segment
|
||||
processed_parts.append(_WIKILINK_RE.sub(
|
||||
lambda m: _resolve_wikilink(m, page_src),
|
||||
part
|
||||
))
|
||||
result_lines.append(''.join(processed_parts))
|
||||
|
||||
return '\n'.join(result_lines)
|
||||
|
||||
|
||||
def _resolve_wikilink(match: re.Match, page_src: str) -> str:
|
||||
"""Resolve a single wiki-link match to markdown."""
|
||||
is_embed = match.group(1) == '!'
|
||||
inner = match.group(2).strip()
|
||||
|
||||
# Parse: name|display and name#anchor
|
||||
display = None
|
||||
anchor = ''
|
||||
|
||||
if '|' in inner:
|
||||
name_part, display = inner.split('|', 1)
|
||||
display = display.strip()
|
||||
name_part = name_part.strip()
|
||||
else:
|
||||
name_part = inner
|
||||
|
||||
if '#' in name_part:
|
||||
name_part, anchor = name_part.split('#', 1)
|
||||
anchor = anchor.strip()
|
||||
|
||||
name_part = name_part.strip()
|
||||
|
||||
# Resolve the target file
|
||||
target_src = _resolve_link(name_part)
|
||||
|
||||
if target_src is None:
|
||||
# Try with .md appended
|
||||
target_src = _resolve_link(name_part + '.md')
|
||||
|
||||
if target_src is None:
|
||||
# Try common image extensions
|
||||
for ext in ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp']:
|
||||
target_src = _resolve_link(name_part + ext)
|
||||
if target_src:
|
||||
break
|
||||
|
||||
if target_src is None:
|
||||
logger.warning(f"[wikilinks] Unresolved wiki-link: [[{inner}]] in {page_src}")
|
||||
return match.group(0) # Leave as-is
|
||||
|
||||
url = _compute_relative_path(page_src, target_src, anchor)
|
||||
|
||||
if is_embed:
|
||||
# Image embed: ![[image.png]] or ![[image.png|alt text]]
|
||||
alt = display or name_part
|
||||
return f''
|
||||
else:
|
||||
# Document link: [[page]] or [[page|Display Text]]
|
||||
label = display or (name_part + (f'#{anchor}' if anchor else ''))
|
||||
return f'[{label}]({url})'
|
||||
|
||||
|
||||
def on_page_markdown(markdown: str, page: Any, config: Dict[str, Any], files: Any) -> str:
|
||||
"""Process wiki-links in page markdown content."""
|
||||
# Quick check — skip pages without wiki-links
|
||||
if '[[' not in markdown:
|
||||
return markdown
|
||||
|
||||
return _replace_wikilinks(markdown, page.file.src_path)
|
||||
@ -5,3 +5,10 @@ hide:
|
||||
- toc
|
||||
title: "Test Page"
|
||||
---
|
||||
---
|
||||
template: test-page.html
|
||||
hide:
|
||||
- navigation
|
||||
- toc
|
||||
title: "Test Page"
|
||||
---
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
Testing page.
|
||||
|
||||
[[test-page]]]]
|
||||
|
||||
|
||||
<div class="photo-block" data-photo-id="1" data-size="large" data-caption="" data-link-to-gallery="true" data-alignment="center">Loading...</div>
|
||||
|
||||
|
||||
@ -1,2 +1,19 @@
|
||||
# testing
|
||||
# testing
|
||||
|
||||
## Wiki-Link Tests
|
||||
|
||||
- Doc link: [[installation]]
|
||||
- Doc link with display text: [[installation|Install Guide]]
|
||||
- Doc link with anchor: [[installation#prerequisites]]
|
||||
- Image embed: ![[logo.png]]
|
||||
- Image with alt: ![[logo.png|Site Logo]]
|
||||
- Code block (should NOT be converted):
|
||||
|
||||
```
|
||||
[[this-should-stay-as-is]]
|
||||
```
|
||||
|
||||
- Inline code (should NOT be converted): `[[not-a-link]]`
|
||||
- Unresolved link (should stay as-is): [[nonexistent-page]]
|
||||
|
||||
|
||||
@ -94,6 +94,7 @@ extra_javascript:
|
||||
hooks:
|
||||
- docs/hooks/repo_widget_hook.py
|
||||
- docs/hooks/env_config_hook.py
|
||||
- docs/hooks/wikilinks_hook.py
|
||||
|
||||
# Markdown Extensions
|
||||
markdown_extensions:
|
||||
|
||||
@ -200,6 +200,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -263,6 +269,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -366,6 +386,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -608,6 +718,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -619,37 +831,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="/." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="/assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -677,18 +860,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -727,106 +901,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="/." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="/docs/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="/blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
"stars_count": 0,
|
||||
"forks_count": 0,
|
||||
"open_issues_count": 23,
|
||||
"updated_at": "2026-03-08T18:11:30-06:00",
|
||||
"updated_at": "2026-03-09T12:23:17-06:00",
|
||||
"created_at": "2025-05-28T14:54:59-06:00",
|
||||
"clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git",
|
||||
"ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T18:11:30-06:00"
|
||||
"last_build_update": "2026-03-09T12:23:17-06:00"
|
||||
}
|
||||
@ -4,10 +4,10 @@
|
||||
"description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.",
|
||||
"html_url": "https://github.com/anthropics/claude-code",
|
||||
"language": "Shell",
|
||||
"stars_count": 75344,
|
||||
"forks_count": 6074,
|
||||
"open_issues_count": 5793,
|
||||
"updated_at": "2026-03-09T00:15:16Z",
|
||||
"stars_count": 75798,
|
||||
"forks_count": 6114,
|
||||
"open_issues_count": 5868,
|
||||
"updated_at": "2026-03-09T21:59:41Z",
|
||||
"created_at": "2025-02-22T17:41:21Z",
|
||||
"clone_url": "https://github.com/anthropics/claude-code.git",
|
||||
"ssh_url": "git@github.com:anthropics/claude-code.git",
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "VS Code in the browser",
|
||||
"html_url": "https://github.com/coder/code-server",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 76540,
|
||||
"forks_count": 6539,
|
||||
"stars_count": 76554,
|
||||
"forks_count": 6541,
|
||||
"open_issues_count": 169,
|
||||
"updated_at": "2026-03-08T21:32:19Z",
|
||||
"updated_at": "2026-03-09T20:21:15Z",
|
||||
"created_at": "2019-02-27T16:50:41Z",
|
||||
"clone_url": "https://github.com/coder/code-server.git",
|
||||
"ssh_url": "git@github.com:coder/code-server.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-06T12:59:10Z"
|
||||
"last_build_update": "2026-03-09T19:30:51Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.",
|
||||
"html_url": "https://github.com/gethomepage/homepage",
|
||||
"language": "JavaScript",
|
||||
"stars_count": 28793,
|
||||
"forks_count": 1811,
|
||||
"stars_count": 28818,
|
||||
"forks_count": 1812,
|
||||
"open_issues_count": 1,
|
||||
"updated_at": "2026-03-08T23:44:14Z",
|
||||
"updated_at": "2026-03-09T21:38:58Z",
|
||||
"created_at": "2022-08-24T07:29:42Z",
|
||||
"clone_url": "https://github.com/gethomepage/homepage.git",
|
||||
"ssh_url": "git@github.com:gethomepage/homepage.git",
|
||||
"default_branch": "dev",
|
||||
"last_build_update": "2026-03-08T12:16:51Z"
|
||||
"last_build_update": "2026-03-09T17:02:06Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD",
|
||||
"html_url": "https://github.com/go-gitea/gitea",
|
||||
"language": "Go",
|
||||
"stars_count": 54180,
|
||||
"forks_count": 6438,
|
||||
"open_issues_count": 2846,
|
||||
"updated_at": "2026-03-08T23:25:42Z",
|
||||
"stars_count": 54194,
|
||||
"forks_count": 6448,
|
||||
"open_issues_count": 2850,
|
||||
"updated_at": "2026-03-09T21:55:30Z",
|
||||
"created_at": "2016-11-01T02:13:26Z",
|
||||
"clone_url": "https://github.com/go-gitea/gitea.git",
|
||||
"ssh_url": "git@github.com:go-gitea/gitea.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T20:49:59Z"
|
||||
"last_build_update": "2026-03-09T10:12:24Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.",
|
||||
"html_url": "https://github.com/knadh/listmonk",
|
||||
"language": "Go",
|
||||
"stars_count": 19221,
|
||||
"stars_count": 19236,
|
||||
"forks_count": 1945,
|
||||
"open_issues_count": 99,
|
||||
"updated_at": "2026-03-08T23:08:55Z",
|
||||
"updated_at": "2026-03-09T21:14:22Z",
|
||||
"created_at": "2019-06-26T05:08:39Z",
|
||||
"clone_url": "https://github.com/knadh/listmonk.git",
|
||||
"ssh_url": "git@github.com:knadh/listmonk.git",
|
||||
"default_branch": "master",
|
||||
"last_build_update": "2026-03-08T14:04:45Z"
|
||||
"last_build_update": "2026-03-09T03:05:44Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Create & scan cute qr codes easily \ud83d\udc7e",
|
||||
"html_url": "https://github.com/lyqht/mini-qr",
|
||||
"language": "Vue",
|
||||
"stars_count": 1898,
|
||||
"forks_count": 240,
|
||||
"open_issues_count": 21,
|
||||
"updated_at": "2026-03-08T15:02:09Z",
|
||||
"stars_count": 1897,
|
||||
"forks_count": 241,
|
||||
"open_issues_count": 22,
|
||||
"updated_at": "2026-03-09T15:15:51Z",
|
||||
"created_at": "2023-04-21T14:20:14Z",
|
||||
"clone_url": "https://github.com/lyqht/mini-qr.git",
|
||||
"ssh_url": "git@github.com:lyqht/mini-qr.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-05T13:18:42Z"
|
||||
"last_build_update": "2026-03-09T07:31:47Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.",
|
||||
"html_url": "https://github.com/n8n-io/n8n",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 178152,
|
||||
"forks_count": 55558,
|
||||
"open_issues_count": 1405,
|
||||
"updated_at": "2026-03-09T00:14:18Z",
|
||||
"stars_count": 178343,
|
||||
"forks_count": 55592,
|
||||
"open_issues_count": 1414,
|
||||
"updated_at": "2026-03-09T21:52:13Z",
|
||||
"created_at": "2019-06-22T09:24:21Z",
|
||||
"clone_url": "https://github.com/n8n-io/n8n.git",
|
||||
"ssh_url": "git@github.com:n8n-io/n8n.git",
|
||||
"default_branch": "master",
|
||||
"last_build_update": "2026-03-09T00:10:51Z"
|
||||
"last_build_update": "2026-03-09T21:33:49Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 A Free & Self-hostable Airtable Alternative",
|
||||
"html_url": "https://github.com/nocodb/nocodb",
|
||||
"language": "TypeScript",
|
||||
"stars_count": 62384,
|
||||
"forks_count": 4655,
|
||||
"open_issues_count": 627,
|
||||
"updated_at": "2026-03-08T23:36:57Z",
|
||||
"stars_count": 62431,
|
||||
"forks_count": 4659,
|
||||
"open_issues_count": 629,
|
||||
"updated_at": "2026-03-09T19:42:51Z",
|
||||
"created_at": "2017-10-29T18:51:48Z",
|
||||
"clone_url": "https://github.com/nocodb/nocodb.git",
|
||||
"ssh_url": "git@github.com:nocodb/nocodb.git",
|
||||
"default_branch": "develop",
|
||||
"last_build_update": "2026-03-07T10:48:26Z"
|
||||
"last_build_update": "2026-03-09T12:25:14Z"
|
||||
}
|
||||
@ -4,13 +4,13 @@
|
||||
"description": "Get up and running with Kimi-K2.5, GLM-5, MiniMax, DeepSeek, gpt-oss, Qwen, Gemma and other models.",
|
||||
"html_url": "https://github.com/ollama/ollama",
|
||||
"language": "Go",
|
||||
"stars_count": 164479,
|
||||
"forks_count": 14834,
|
||||
"open_issues_count": 2613,
|
||||
"updated_at": "2026-03-09T00:13:40Z",
|
||||
"stars_count": 164642,
|
||||
"forks_count": 14860,
|
||||
"open_issues_count": 2609,
|
||||
"updated_at": "2026-03-09T21:37:49Z",
|
||||
"created_at": "2023-06-26T19:39:32Z",
|
||||
"clone_url": "https://github.com/ollama/ollama.git",
|
||||
"ssh_url": "git@github.com:ollama/ollama.git",
|
||||
"default_branch": "main",
|
||||
"last_build_update": "2026-03-08T06:32:28Z"
|
||||
"last_build_update": "2026-03-09T21:19:34Z"
|
||||
}
|
||||
@ -4,10 +4,10 @@
|
||||
"description": "Documentation that simply works",
|
||||
"html_url": "https://github.com/squidfunk/mkdocs-material",
|
||||
"language": "Python",
|
||||
"stars_count": 26208,
|
||||
"forks_count": 4048,
|
||||
"stars_count": 26215,
|
||||
"forks_count": 4051,
|
||||
"open_issues_count": 2,
|
||||
"updated_at": "2026-03-08T17:43:54Z",
|
||||
"updated_at": "2026-03-09T19:46:16Z",
|
||||
"created_at": "2016-01-28T22:09:23Z",
|
||||
"clone_url": "https://github.com/squidfunk/mkdocs-material.git",
|
||||
"ssh_url": "git@github.com:squidfunk/mkdocs-material.git",
|
||||
|
||||
@ -222,6 +222,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -285,6 +291,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -388,6 +408,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -630,6 +740,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -641,37 +853,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Blog
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -699,18 +882,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -749,108 +923,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href=".." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../docs/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="./" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -215,6 +215,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -278,6 +284,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -381,6 +401,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -623,6 +733,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -634,37 +846,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Signing in...
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -692,18 +875,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -742,106 +916,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../docs/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Advocacy Campaigns
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Email Queue
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Advocacy
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Representatives
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Response Moderation
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Email Templates
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Broadcast
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Newsletter (Listmonk)
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
SMS Campaigns
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Dashboard
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Admin Guide
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Areas (Cuts)
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Canvassing
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Data Quality
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Map & Canvassing
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Locations
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Map Settings
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Shifts
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Gallery Ads
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Analytics
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Curated Gallery
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Media
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Library
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Moderation
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Donations
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Payments
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Plans
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Products
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Payment Settings
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
People & Access
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
CrowdSec & Security
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Services
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Integrations
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Monitoring
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Tunnel (Pangolin)
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
User Provisioning
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Platform Settings
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Documentation
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Public Homepage
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Web Content
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Landing Pages
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Navigation Settings
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
API Reference
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Architecture
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Deployment
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Control Panel (CCP)
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Environment Variables
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Features at a Glance
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
First Steps
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Getting Started
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Installation
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Services Overview
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Updates & Upgrades
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Documentation
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href=".." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="./" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Philosophy
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
@ -224,6 +224,12 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span class="cm-header-nav__label">Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span class="cm-header-nav__label">Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span class="cm-header-nav__label">Docs</span></a>
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@ -287,6 +293,20 @@
|
||||
<a href="#" data-path="/pages" class="cm-header-nav__mobile-link" data-nav-id="pages"><span class="material-icons-outlined">description</span><span>Pages</span></a>
|
||||
<a href="/" class="cm-header-nav__mobile-link" data-nav-id="landing"><span class="material-icons-outlined">language</span><span>Website</span></a>
|
||||
<a href="/docs/" class="cm-header-nav__mobile-link" data-nav-id="docs"><span class="material-icons-outlined">menu_book</span><span>Docs</span></a>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@ -390,6 +410,96 @@
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// On desktop, Material's search input is always visible (overflow from collapsed
|
||||
// header). Typing directly into it triggers the search worker but never checks
|
||||
// the __search checkbox, so the results panel stays hidden. We fix this by
|
||||
// checking the checkbox on input focus/input events.
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Activate search when the Material input is focused or typed into directly.
|
||||
// Uses event delegation because the search input is rendered after this
|
||||
// script (announce block runs before header block).
|
||||
document.addEventListener('focusin', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target && e.target.classList && e.target.classList.contains('md-search__input')) {
|
||||
if (!searchToggle.checked) {
|
||||
searchToggle.checked = true;
|
||||
searchToggle.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@ -632,6 +742,108 @@
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</div>
|
||||
@ -643,37 +855,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
|
||||
|
||||
<img src="../../assets/logo.png" alt="logo">
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Changemaker Lite
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Services
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
@ -701,18 +884,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
</div>
|
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
@ -751,108 +925,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
changemaker.lite
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../.." class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Home
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item md-tabs__item--active">
|
||||
<a href="../" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5-1.65 0-3.35.3-4.75 1.05-.1.05-.15.05-.25.05-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1 1.11-.35 2.33-.5 3.5-.5 1.95 0 4.05.4 5.5 1.5 1.45-1.1 3.55-1.5 5.5-1.5 1.17 0 2.39.15 3.5.5.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5-.1 0-.15 0-.25-.05-1.4-.75-3.1-1.05-4.75-1.05-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1 .91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99 1.04 0 1.88.08 2.5.24z"/></svg>
|
||||
|
||||
|
||||
Docs
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-tabs__item">
|
||||
<a href="../../blog/" class="md-tabs__link">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Blog
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user