Major Improvements

Too numerous to mention
This commit is contained in:
2026-03-08 11:27:25 -04:00
parent c44db8cd3c
commit d74de05065
9 changed files with 693 additions and 155 deletions

View File

@@ -1,5 +1,244 @@
import { withPluginApi } from "discourse/lib/plugin-api";
// Setting descriptions — injected into the admin DOM since the newer
// plugin settings page does not render .desc elements automatically.
const DESCRIPTIONS = {
// ── Master Switch ──
community_landing_enabled: "Enable the community landing page for logged-out visitors.",
// ── Layout ──
section_order: "Order of content sections. Drag to reorder. Available: hero, stats, about, participation, topics, groups, app_cta.",
custom_css: "Raw CSS injected after all plugin styles. Use for overrides and tweaks. No style tags needed.",
// ── SEO & Meta ──
meta_description: "Meta description for search engines and social sharing. If blank, the hero subtitle is used.",
og_image_url: "Open Graph image URL for social sharing (1200×630px recommended). If blank, the site logo is used.",
favicon_url: "Custom favicon URL (.ico, .png, .svg). If blank, the browser default is used.",
json_ld_enabled: "Add JSON-LD structured data (Organization + WebSite schema) for search engines.",
// ── Branding: Logo ──
logo_dark_url: "Logo image URL for dark mode. Shown in navbar and footer. Leave blank to show site name as text.",
logo_light_url: "Logo image URL for light mode. If not set, the dark logo is used for both themes.",
logo_height: "Logo height in pixels (1680). Applies to both navbar and footer logos.",
footer_logo_url: "Override logo for the footer only. If not set, the navbar logo is reused.",
// ── Colors ──
accent_color: "Primary accent color: buttons, links, highlights, gradients, stat icons. Hex value.",
accent_hover_color: "Accent color on hover. Should be slightly lighter or darker than the accent.",
dark_bg_color: "Page background color for dark mode.",
light_bg_color: "Page background color for light mode.",
orb_color: "Color of decorative background orbs. Leave blank to use the accent color.",
orb_opacity: "Opacity of the background orbs (0100). Default: 50.",
// ── Scroll Animations ──
scroll_animation: "How sections animate into view on scroll: fade_up, fade_in, slide_left, slide_right, zoom_in, flip_up, or none.",
staggered_reveal_enabled: "Animate child elements (cards, stats) with a staggered delay for a cascading reveal effect.",
dynamic_background_enabled: "Enable parallax background orbs that drift as the user scrolls.",
mouse_parallax_enabled: "Enable subtle parallax movement of background elements in response to mouse position.",
scroll_progress_enabled: "Show a thin progress bar at the top of the page indicating scroll position.",
// ── Fonts ──
google_font_name: "Google Font family for body text. Must match exact Google Fonts name (e.g. 'Inter', 'Poppins'). Default: Outfit.",
title_font_name: "Separate Google Font for titles and headings. Leave blank to use the body font.",
// ── Icons ──
fontawesome_enabled: "Load FontAwesome 6 Free icons from CDN for use on buttons.",
// ── Navbar ──
navbar_signin_label: "Text for the sign-in link in the navbar.",
navbar_signin_enabled: "Show the sign-in link in the navbar.",
navbar_signin_color_dark: "Sign-in link color for dark mode. Leave blank for default.",
navbar_signin_color_light: "Sign-in link color for light mode.",
navbar_join_label: "Text for the join/register CTA button in the navbar.",
navbar_join_enabled: "Show the join/register button in the navbar.",
navbar_join_color_dark: "Join button background color for dark mode. Leave blank for accent color.",
navbar_join_color_light: "Join button background color for light mode.",
navbar_bg_color: "Custom navbar background when scrolled. Leave blank for frosted glass effect.",
navbar_border_style: "Border style at the bottom of the navbar when scrolled.",
navbar_signin_icon: "FontAwesome icon name for sign-in (e.g. 'right-to-bracket'). Requires FontAwesome enabled.",
navbar_signin_icon_position: "Show the sign-in icon before or after the label.",
navbar_join_icon: "FontAwesome icon name for join button (e.g. 'user-plus'). Requires FontAwesome enabled.",
navbar_join_icon_position: "Show the join icon before or after the label.",
social_twitter_url: "Twitter / X profile URL. Leave blank to hide. Icons appear in navbar before auth buttons.",
social_facebook_url: "Facebook page or profile URL. Leave blank to hide.",
social_instagram_url: "Instagram profile URL. Leave blank to hide.",
social_youtube_url: "YouTube channel URL. Leave blank to hide.",
social_tiktok_url: "TikTok profile URL. Leave blank to hide.",
social_github_url: "GitHub organization or profile URL. Leave blank to hide.",
// ── Hero ──
hero_title: "Main headline text in the hero section.",
hero_title_size: "Hero title font size in pixels. 0 = use default responsive size.",
hero_accent_word: "Which word gets the accent shimmer. 0 = last word, 1 = first, 2 = second, etc.",
hero_subtitle: "Supporting text below the headline. Describe your community's purpose.",
hero_card_enabled: "Display hero content inside a rounded card with border and shadow.",
hero_image_first: "Show hero image above text on mobile / left on desktop. Off = text first.",
hero_background_image_url: "Full-bleed background image behind the hero. In card mode, fills the card with overlay.",
hero_image_urls: "Images for the right side of the hero. Up to 5 URLs — one shown randomly per page load.",
hero_image_max_height: "Maximum height for the hero image in pixels (1001200).",
hero_primary_button_enabled: "Show the primary CTA button in the hero.",
hero_primary_button_label: "Text on the primary (filled, accent-colored) CTA button.",
hero_primary_button_url: "URL the primary button links to. Relative path or absolute URL.",
hero_secondary_button_enabled: "Show the secondary CTA button in the hero.",
hero_secondary_button_label: "Text on the secondary (outlined) CTA button.",
hero_secondary_button_url: "URL the secondary button links to.",
hero_primary_button_icon: "FontAwesome icon for primary button (e.g. 'rocket'). Leave blank for no icon.",
hero_primary_button_icon_position: "Show the primary button icon before or after the label.",
hero_secondary_button_icon: "FontAwesome icon for secondary button. Leave blank for no icon.",
hero_secondary_button_icon_position: "Show the secondary button icon before or after the label.",
hero_primary_btn_color_dark: "Primary button background for dark mode. Leave blank for accent color.",
hero_primary_btn_color_light: "Primary button background for light mode.",
hero_secondary_btn_color_dark: "Secondary button background for dark mode. Leave blank for glass style.",
hero_secondary_btn_color_light: "Secondary button background for light mode.",
hero_video_url: "Hero video URL (MP4 or YouTube). Play button opens a lightbox modal.",
hero_video_button_color: "Custom color for the video play button. Leave blank for accent color.",
hero_video_blur_on_hover: "Blur the hero image when hovering the play button.",
hero_bg_dark: "Hero section background for dark mode. Leave blank for default.",
hero_bg_light: "Hero section background for light mode.",
hero_min_height: "Minimum hero section height in pixels. 0 = auto height.",
hero_border_style: "Border style at the bottom of the hero section.",
hero_card_bg_dark: "Hero card overlay background for dark mode. Only in card mode.",
hero_card_bg_light: "Hero card overlay background for light mode.",
hero_card_opacity: "Hero card background opacity (01). Lower = more transparent. Default: 0.85.",
// ── Contributors (Hero Creators) ──
contributors_enabled: "Show top 3 creators in the hero with gold, silver, bronze badges.",
contributors_title: "Heading above the creators list.",
contributors_title_enabled: "Show the heading above the creators list.",
contributors_count_label: "Label before each creator's count (e.g. 'Cheers'). Blank = no prefix.",
contributors_count_label_enabled: "Show the count label prefix before activity counts.",
contributors_alignment: "Horizontal alignment of the creators list: center or left.",
contributors_pill_max_width: "Max width per creator pill card in pixels (200600).",
contributors_pill_bg_dark: "Creator pill background for dark mode. Leave blank for glass styling.",
contributors_pill_bg_light: "Creator pill background for light mode.",
contributors_days: "Lookback period in days for calculating top contributors.",
contributors_count: "Number of top contributors to fetch (top 3 in hero, 410 in Participation).",
// ── Participation ──
participation_enabled: "Show Participation section: testimonial cards with leaderboard bios (positions 410).",
participation_title_enabled: "Show heading above participation cards.",
participation_title: "Heading text above participation cards.",
participation_bio_max_length: "Max characters from each user's bio (50500). Longer bios are truncated.",
participation_icon_color: "Color for the decorative quote icon on cards. Leave blank for accent color.",
participation_card_bg_dark: "Participation card background for dark mode.",
participation_card_bg_light: "Participation card background for light mode.",
participation_bg_dark: "Section background for dark mode. Leave blank for default.",
participation_bg_light: "Section background for light mode.",
participation_min_height: "Minimum section height in pixels. 0 = auto.",
participation_border_style: "Border style at the bottom of the section.",
participation_title_size: "Section title font size in pixels. 0 = use default.",
// ── Stats ──
stats_enabled: "Show the stats section with live community counters.",
stat_labels_enabled: "Show text labels below stat counters (e.g. 'Members'). Off = numbers and icons only.",
stats_title_enabled: "Show section heading above the stats row.",
stats_title: "Section heading text above the stats.",
stats_title_size: "Stats title font size in pixels. 0 = use default.",
stat_card_style: "Stat card style: rectangle, rounded, pill, or minimal (no background).",
stat_icon_color: "Color for stat counter icons.",
stat_icon_bg_color: "Background behind each stat icon. Leave blank for subtle accent tint.",
stat_icon_shape: "Icon background shape: circle or rounded square.",
stat_counter_color: "Color for stat counter numbers. Leave blank for default text color.",
stat_members_label: "Custom label for the Members stat.",
stat_topics_label: "Custom label for the Topics stat.",
stat_posts_label: "Custom label for the Posts stat.",
stat_likes_label: "Custom label for the Likes stat.",
stat_chats_label: "Custom label for the Chats stat. Shows chat messages if Chat plugin is active.",
stat_round_numbers: "Round large numbers: 1000 → 1K, 12345 → 12.3K, 1234567 → 1.2M.",
stat_card_bg_dark: "Stat card background for dark mode.",
stat_card_bg_light: "Stat card background for light mode.",
stats_bg_dark: "Section background for dark mode. Leave blank for default.",
stats_bg_light: "Section background for light mode.",
stats_min_height: "Minimum section height in pixels. 0 = auto.",
stats_border_style: "Border style at the bottom of the stats section.",
// ── About ──
about_enabled: "Show the About section: card with heading, quote icon, description, and author attribution.",
about_heading_enabled: "Show the bold heading at the top of the About card.",
about_heading: "Heading text at the top of the About card (e.g. 'About Community').",
about_title: "Author or community name in the card's bottom attribution.",
about_title_size: "About heading font size in pixels. 0 = use default.",
about_role: "Subtitle below author name (e.g. 'Community Manager'). Blank = site name.",
about_body: "Main body text. Supports HTML: p, a, strong, em, ul, li, br.",
about_image_url: "Avatar image next to author name. Square images work best.",
about_card_color_dark: "About card background for dark mode.",
about_card_color_light: "About card background for light mode.",
about_background_image_url: "Background image on the card. Use a subtle pattern or texture.",
about_bg_dark: "Section background for dark mode. Leave blank for default.",
about_bg_light: "Section background for light mode.",
about_min_height: "Minimum section height in pixels. 0 = auto.",
about_border_style: "Border style at the bottom of the about section.",
// ── Trending ──
topics_enabled: "Show Trending Discussions: scrollable row of active topic cards with live data.",
topics_title_enabled: "Show heading above the topic cards.",
topics_title: "Heading text above the topic cards.",
topics_title_size: "Trending title font size in pixels. 0 = use default.",
topics_count: "Number of trending topic cards to display.",
topics_card_bg_dark: "Topic card background for dark mode.",
topics_card_bg_light: "Topic card background for light mode.",
topics_bg_dark: "Section background for dark mode. Leave blank for default.",
topics_bg_light: "Section background for light mode.",
topics_min_height: "Minimum section height in pixels. 0 = auto.",
topics_border_style: "Border style at the bottom of the trending section.",
// ── Spaces ──
groups_enabled: "Show Community Spaces: grid of group cards with icon, name, and member count.",
groups_title_enabled: "Show heading above group cards.",
groups_title: "Heading text above group cards.",
groups_title_size: "Spaces title font size in pixels. 0 = use default.",
groups_count: "Number of group cards to display.",
groups_selected: "Show only specific groups. Enter names separated by pipes (e.g. designers|developers). Blank = auto-select.",
groups_show_description: "Show group description text below the group name on each card.",
groups_description_max_length: "Max characters for group descriptions (30500). Longer text is truncated.",
groups_card_bg_dark: "Space card background for dark mode.",
groups_card_bg_light: "Space card background for light mode.",
groups_bg_dark: "Section background for dark mode. Leave blank for default.",
groups_bg_light: "Section background for light mode.",
groups_min_height: "Minimum section height in pixels. 0 = auto.",
groups_border_style: "Border style at the bottom of the spaces section.",
// ── FAQ ──
faq_enabled: "Show FAQ accordion alongside the Spaces section. One item opens at a time.",
faq_title_enabled: "Show heading above the FAQ accordion.",
faq_title: "Heading text above the FAQ.",
faq_title_size: "FAQ title font size in pixels. 0 = use default.",
faq_items: 'FAQ items as JSON array: [{\"q\":\"Question\",\"a\":\"Answer\"}]. HTML supported in answers.',
faq_card_bg_dark: "FAQ card background for dark mode.",
faq_card_bg_light: "FAQ card background for light mode.",
// ── App CTA ──
show_app_ctas: "Show App Download CTA: gradient banner with headline, badges, and promo image.",
ios_app_url: "Apple App Store URL. Leave blank to hide iOS badge.",
android_app_url: "Google Play Store URL. Leave blank to hide Android badge.",
ios_app_badge_image_url: "Custom iOS badge image. Leave blank for default.",
android_app_badge_image_url: "Custom Android badge image. Leave blank for default.",
app_badge_height: "Badge height in pixels (3080).",
app_badge_style: "Badge border-radius: rounded, pill, or square.",
app_cta_headline: "Bold headline in the app download banner.",
app_cta_title_size: "App CTA headline font size in pixels. 0 = use default.",
app_cta_subtext: "Supporting text below the headline.",
app_cta_gradient_start_dark: "Gradient start color for dark mode. Leave blank for accent.",
app_cta_gradient_start_light: "Gradient start color for light mode.",
app_cta_gradient_mid_dark: "Gradient middle color for dark mode.",
app_cta_gradient_mid_light: "Gradient middle color for light mode.",
app_cta_gradient_end_dark: "Gradient end color for dark mode.",
app_cta_gradient_end_light: "Gradient end color for light mode.",
app_cta_image_url: "Promo image on the right (e.g. phone mockup). PNG for transparent bg.",
app_cta_bg_dark: "Section background for dark mode. Leave blank for default.",
app_cta_bg_light: "Section background for light mode.",
app_cta_min_height: "Minimum section height in pixels. 0 = auto.",
app_cta_border_style: "Border style at the bottom of the app CTA section.",
// ── Footer ──
footer_description: "Description paragraph above the footer bar.",
footer_text: "Optional HTML text inside the footer bar. Supports: p, a, strong, em, ul, li, br.",
footer_links: 'Footer links as JSON array: [{\"label\":\"Terms\",\"url\":\"/tos\"}].',
footer_bg_dark: "Footer background for dark mode. Leave blank for default.",
footer_bg_light: "Footer background for light mode.",
footer_border_style: "Border style at the top of the footer bar.",
};
const TABS = [
{
id: "settings",
@@ -10,8 +249,10 @@ const TABS = [
"meta_description", "og_image_url", "favicon_url", "json_ld_enabled",
"logo_dark_url", "logo_light_url", "logo_height", "footer_logo_url",
"accent_color", "accent_hover_color", "dark_bg_color", "light_bg_color",
"orb_color", "orb_opacity",
"scroll_animation", "staggered_reveal_enabled", "dynamic_background_enabled",
"mouse_parallax_enabled", "scroll_progress_enabled"
"mouse_parallax_enabled", "scroll_progress_enabled",
"google_font_name", "title_font_name", "fontawesome_enabled"
])
},
{
@@ -22,6 +263,8 @@ const TABS = [
"navbar_signin_color_dark", "navbar_signin_color_light",
"navbar_join_label", "navbar_join_enabled",
"navbar_join_color_dark", "navbar_join_color_light",
"navbar_signin_icon", "navbar_signin_icon_position",
"navbar_join_icon", "navbar_join_icon_position",
"navbar_bg_color", "navbar_border_style",
"social_twitter_url", "social_facebook_url", "social_instagram_url",
"social_youtube_url", "social_tiktok_url", "social_github_url"
@@ -31,11 +274,13 @@ const TABS = [
id: "hero",
label: "Hero",
settings: new Set([
"hero_title", "hero_accent_word", "hero_subtitle",
"hero_title", "hero_accent_word", "hero_subtitle", "hero_title_size",
"hero_card_enabled", "hero_image_first",
"hero_background_image_url", "hero_image_urls", "hero_image_max_height",
"hero_primary_button_enabled", "hero_primary_button_label", "hero_primary_button_url",
"hero_primary_button_icon", "hero_primary_button_icon_position",
"hero_secondary_button_enabled", "hero_secondary_button_label", "hero_secondary_button_url",
"hero_secondary_button_icon", "hero_secondary_button_icon_position",
"hero_primary_btn_color_dark", "hero_primary_btn_color_light",
"hero_secondary_btn_color_dark", "hero_secondary_btn_color_light",
"hero_video_url", "hero_video_button_color", "hero_video_blur_on_hover",
@@ -53,7 +298,8 @@ const TABS = [
label: "Participation",
settings: new Set([
"participation_enabled", "participation_title_enabled",
"participation_title", "participation_bio_max_length",
"participation_title", "participation_title_size",
"participation_bio_max_length",
"participation_icon_color",
"participation_card_bg_dark", "participation_card_bg_light",
"participation_bg_dark", "participation_bg_light",
@@ -65,7 +311,7 @@ const TABS = [
label: "Stats",
settings: new Set([
"stats_enabled", "stat_labels_enabled", "stats_title_enabled",
"stats_title", "stat_card_style",
"stats_title", "stats_title_size", "stat_card_style",
"stat_icon_color", "stat_icon_bg_color", "stat_icon_shape", "stat_counter_color",
"stat_members_label", "stat_topics_label", "stat_posts_label",
"stat_likes_label", "stat_chats_label", "stat_round_numbers",
@@ -78,7 +324,7 @@ const TABS = [
label: "About",
settings: new Set([
"about_enabled", "about_heading_enabled", "about_heading",
"about_title", "about_role", "about_body", "about_image_url",
"about_title", "about_title_size", "about_role", "about_body", "about_image_url",
"about_card_color_dark", "about_card_color_light",
"about_background_image_url",
"about_bg_dark", "about_bg_light", "about_min_height", "about_border_style"
@@ -88,21 +334,30 @@ const TABS = [
id: "topics",
label: "Trending",
settings: new Set([
"topics_enabled", "topics_title_enabled", "topics_title", "topics_count",
"topics_enabled", "topics_title_enabled", "topics_title", "topics_title_size",
"topics_count",
"topics_card_bg_dark", "topics_card_bg_light",
"topics_bg_dark", "topics_bg_light", "topics_min_height", "topics_border_style"
])
},
{
id: "groups",
label: "Spaces & FAQ",
label: "Spaces",
settings: new Set([
"groups_enabled", "groups_title_enabled", "groups_title", "groups_count",
"groups_selected",
"groups_enabled", "groups_title_enabled", "groups_title", "groups_title_size",
"groups_count", "groups_selected",
"groups_show_description", "groups_description_max_length",
"groups_card_bg_dark", "groups_card_bg_light",
"groups_bg_dark", "groups_bg_light", "groups_min_height", "groups_border_style",
"faq_enabled", "faq_title_enabled", "faq_title", "faq_items"
"groups_bg_dark", "groups_bg_light", "groups_min_height", "groups_border_style"
])
},
{
id: "faq",
label: "FAQ",
settings: new Set([
"faq_enabled", "faq_title_enabled", "faq_title", "faq_title_size",
"faq_items",
"faq_card_bg_dark", "faq_card_bg_light"
])
},
{
@@ -112,7 +367,7 @@ const TABS = [
"show_app_ctas", "ios_app_url", "android_app_url",
"ios_app_badge_image_url", "android_app_badge_image_url",
"app_badge_height", "app_badge_style",
"app_cta_headline", "app_cta_subtext",
"app_cta_headline", "app_cta_title_size", "app_cta_subtext",
"app_cta_gradient_start_dark", "app_cta_gradient_start_light",
"app_cta_gradient_mid_dark", "app_cta_gradient_mid_light",
"app_cta_gradient_end_dark", "app_cta_gradient_end_light",
@@ -156,6 +411,8 @@ const BG_PAIRS = [
// Spaces
["groups_card_bg_dark", "groups_card_bg_light"],
["groups_bg_dark", "groups_bg_light"],
// FAQ
["faq_card_bg_dark", "faq_card_bg_light"],
// App CTA
["app_cta_gradient_start_dark", "app_cta_gradient_start_light"],
["app_cta_gradient_mid_dark", "app_cta_gradient_mid_light"],
@@ -185,8 +442,6 @@ function applyTabFilter() {
if (!tab) return;
container.querySelectorAll(".row.setting[data-setting]").forEach((row) => {
// Skip rows inside a merge wrapper — handled at wrapper level
if (row.closest(".cl-merge-wrapper")) return;
const name = row.getAttribute("data-setting");
row.classList.toggle(
"cl-tab-hidden",
@@ -194,17 +449,6 @@ function applyTabFilter() {
);
});
// Handle merge wrappers — show/hide based on dark row's setting
container.querySelectorAll(".cl-merge-wrapper").forEach((wrapper) => {
const darkRow = wrapper.querySelector(".cl-merged-dark");
if (!darkRow) return;
const name = darkRow.getAttribute("data-setting");
wrapper.classList.toggle(
"cl-tab-hidden",
!filterActive && !tab.settings.has(name)
);
});
// Update filter-active dimming on native nav or standalone tab bar
const nativeNav = document.querySelector(".d-nav-submenu__tabs");
if (nativeNav) {
@@ -294,15 +538,9 @@ function cleanupTabs() {
el.classList.remove("cl-tab-hidden");
});
// Unwrap merge wrappers — restore rows to their original position
container.querySelectorAll(".cl-merge-wrapper").forEach((wrapper) => {
const parent = wrapper.parentNode;
while (wrapper.firstChild) {
const child = wrapper.firstChild;
child.classList.remove("cl-merged-dark", "cl-merged-light");
parent.insertBefore(child, wrapper);
}
wrapper.remove();
// Remove merge classes
container.querySelectorAll(".cl-merged-dark, .cl-merged-light").forEach((el) => {
el.classList.remove("cl-merged-dark", "cl-merged-light");
});
}
@@ -311,10 +549,37 @@ function cleanupTabs() {
filterActive = false;
}
/**
* Inject description text into each setting row.
* The newer Discourse plugin admin page doesn't render .desc elements,
* so we add them from the DESCRIPTIONS map.
*/
function injectDescriptions() {
const container = getContainer();
if (!container) return;
container.querySelectorAll(".row.setting[data-setting]").forEach((row) => {
const name = row.getAttribute("data-setting");
const text = DESCRIPTIONS[name];
if (!text) return;
const valueDiv = row.querySelector(".setting-value");
if (!valueDiv) return;
// Already injected
if (valueDiv.querySelector(".cl-desc")) return;
const desc = document.createElement("div");
desc.className = "cl-desc";
desc.textContent = text;
valueDiv.appendChild(desc);
});
}
/**
* Merge dark/light bg color pairs into a single visual row.
* Uses a CSS wrapper approach — both rows stay intact in the DOM
* (preserving Ember bindings and undo/reset buttons).
* CSS-only approach — elements stay in their original DOM positions
* (preserving Ember bindings, undo/reset buttons, and re-renders).
*/
function mergeBgPairs() {
const container = getContainer();
@@ -349,17 +614,9 @@ function mergeBgPairs() {
lightValue.insertBefore(lbl, lightValue.firstChild);
}
// Wrap both rows in a flex container
const wrapper = document.createElement("div");
wrapper.className = "cl-merge-wrapper";
darkRow.parentNode.insertBefore(wrapper, darkRow);
wrapper.appendChild(darkRow);
wrapper.appendChild(lightRow);
// Mark rows for CSS styling
// Just add classes — NO DOM moves, preserves all Ember bindings
darkRow.classList.add("cl-merged-dark");
lightRow.classList.add("cl-merged-light");
// Light row is NOT hidden — it stays in the DOM with full Ember bindings
});
}
@@ -423,6 +680,7 @@ function buildTabsUI() {
});
container.classList.add("cl-tabs-active");
injectDescriptions();
mergeBgPairs();
applyTabFilter();
return true;
@@ -468,6 +726,7 @@ function buildTabsUI() {
}
container.classList.add("cl-tabs-active");
injectDescriptions();
mergeBgPairs();
applyTabFilter();
return true;

View File

@@ -82,37 +82,76 @@ html.dark-scheme .cl-admin-tabs .cl-admin-tab:hover {
color: var(--primary, #ddd);
}
/* ── Merged dark/light color pairs (wrapper approach) ── */
/* ── Merged dark/light color pairs (CSS-only, no DOM moves) ── */
.cl-merge-wrapper {
display: flex;
gap: 16px;
width: 100%;
padding-bottom: 20px;
}
.cl-merge-wrapper > .row.setting {
flex: 1;
min-width: 0;
.cl-tabs-active .row.setting.cl-merged-dark {
float: left;
width: calc(50% - 8px);
margin-right: 16px;
padding-bottom: 0 !important;
margin-bottom: 0 !important;
}
.cl-tabs-active .row.setting.cl-merged-light {
float: left;
width: calc(50% - 8px);
padding-bottom: 0 !important;
margin-bottom: 20px !important;
}
/* Hide the light row's label + description — dark row's label covers both */
.cl-merge-wrapper > .cl-merged-light > .setting-label {
.cl-tabs-active .row.setting.cl-merged-light > .setting-label {
display: none;
}
.cl-merge-wrapper > .cl-merged-light .desc {
.cl-tabs-active .row.setting.cl-merged-light .desc {
display: none;
}
/* Light row's value area fills the full width since label is hidden */
.cl-tabs-active .cl-merge-wrapper > .cl-merged-light > .setting-value {
.cl-tabs-active .row.setting.cl-merged-light > .setting-value {
width: 100%;
float: none;
}
/* Dark row: label + value stack vertically inside the half-width */
.cl-tabs-active .row.setting.cl-merged-dark > .setting-label {
float: none;
width: 100%;
margin-bottom: 4px;
}
.cl-tabs-active .row.setting.cl-merged-dark > .setting-value {
float: none;
width: 100%;
padding-right: 0;
}
/* Controls (reset/undo) inside merged rows — inline after the color picker */
.cl-tabs-active .row.setting.cl-merged-dark > .setting-controls,
.cl-tabs-active .row.setting.cl-merged-light > .setting-controls {
float: none;
display: inline-block;
margin-top: 4px;
}
/* Clearfix after the light row to restore normal flow */
.cl-tabs-active .row.setting.cl-merged-light::after {
content: "";
display: block;
clear: both;
}
/* Insert a clear break after each pair to prevent stacking issues */
.cl-tabs-active .row.setting.cl-merged-light + .row.setting:not(.cl-merged-light) {
clear: both;
}
/* Also clear when a merged-light is followed by anything else */
.cl-tabs-active .cl-merged-light + *:not(.cl-merged-light):not(.cl-tab-hidden) {
clear: both;
}
.cl-color-col__label {
display: block;
font-size: var(--font-down-1);
@@ -124,12 +163,14 @@ html.dark-scheme .cl-admin-tabs .cl-admin-tab:hover {
}
@media (max-width: 767px) {
.cl-merge-wrapper {
flex-direction: column;
gap: 0;
.cl-tabs-active .row.setting.cl-merged-dark,
.cl-tabs-active .row.setting.cl-merged-light {
float: none;
width: 100%;
margin-right: 0;
}
.cl-merge-wrapper > .cl-merged-light > .setting-label {
.cl-tabs-active .row.setting.cl-merged-light > .setting-label {
display: none;
}
}
@@ -174,7 +215,10 @@ html.dark-scheme .cl-admin-tabs .cl-admin-tab:hover {
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="android_"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="app_"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="social_"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="footer_"] {
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="footer_"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="google_"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting^="title_font"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="fontawesome_enabled"] {
margin-bottom: 20px;
}
@@ -185,6 +229,8 @@ html.dark-scheme .cl-admin-tabs .cl-admin-tab:hover {
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="logo_dark_url"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="accent_color"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="scroll_animation"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="google_font_name"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="fontawesome_enabled"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="social_twitter_url"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="navbar_signin_label"],
.admin-detail:not(.cl-tabs-active) .row.setting[data-setting="hero_title"],
@@ -208,6 +254,8 @@ html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="m
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="logo_dark_url"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="accent_color"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="scroll_animation"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="google_font_name"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="fontawesome_enabled"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="social_twitter_url"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="navbar_signin_label"],
html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="hero_title"],
@@ -298,13 +346,15 @@ html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="f
}
.cl-tabs-active .row.setting .desc,
.cl-tabs-active .row.setting .cl-desc,
.cl-tabs-active .row.setting .validation-error {
padding-top: 3px;
font-size: var(--font-down-1);
line-height: var(--line-height-large);
}
.cl-tabs-active .row.setting .desc {
.cl-tabs-active .row.setting .desc,
.cl-tabs-active .row.setting .cl-desc {
color: var(--primary-medium);
}

View File

@@ -100,7 +100,7 @@
padding: 0;
background: var(--cl-bg);
color: var(--cl-text);
font-family: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-family: var(--cl-font-body, 'Outfit'), -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
@@ -177,7 +177,7 @@ html {
position: absolute;
border-radius: 50%;
filter: blur(100px);
opacity: 0.5;
opacity: var(--cl-orb-opacity, 0.5);
animation: cl-orb-float 20s infinite alternate ease-in-out;
}
@@ -223,6 +223,7 @@ html {
.cl-section-title {
font-size: 1.6rem;
font-weight: 800;
font-family: var(--cl-font-title, var(--cl-font-body, 'Outfit')), sans-serif;
color: var(--cl-text-strong);
margin: 0 0 2.5rem;
letter-spacing: -0.02em;
@@ -380,6 +381,11 @@ html {
font-size: 1.05rem;
}
/* FontAwesome icon spacing inside buttons */
.cl-btn i.fa-solid {
margin: 0 0.35em;
}
/* ═══════════════════════════════════════════════════════════════════
1. NAVBAR — logo left, theme toggle + auth right
═══════════════════════════════════════════════════════════════════ */
@@ -731,6 +737,7 @@ html {
.cl-hero__title {
font-size: clamp(2.5rem, 8vw, 4.5rem);
font-weight: 900;
font-family: var(--cl-font-title, var(--cl-font-body, 'Outfit')), sans-serif;
color: var(--cl-hero-text);
margin: 0 0 1.5rem;
line-height: 0.95;
@@ -1552,6 +1559,15 @@ html {
min-height: 400px;
}
.cl-spaces__col {
display: flex;
flex-direction: column;
}
.cl-spaces__col .cl-section-title {
font-size: 1.3rem;
}
.cl-spaces__full {
display: block;
}
@@ -1663,26 +1679,28 @@ html {
overflow: hidden;
}
/* ── FAQ Accordion ── */
/* ── FAQ Card Accordion ── */
.cl-faq {
display: flex;
flex-direction: column;
gap: 0;
gap: 0.6rem;
}
.cl-faq__title {
font-size: 1.2rem;
font-weight: 800;
color: var(--cl-text-strong);
margin: 0 0 1rem;
.cl-faq__card {
background: var(--cl-faq-card-bg, var(--cl-card));
border: 1px solid var(--cl-border);
border-radius: var(--cl-radius-sm);
padding: 0 1.2rem;
transition: border-color 0.3s, box-shadow 0.3s;
}
.cl-faq__item {
border-bottom: 1px solid var(--cl-border);
.cl-faq__card:hover {
border-color: var(--cl-border-hover);
}
.cl-faq__item:first-of-type {
border-top: 1px solid var(--cl-border);
.cl-faq__card[open] {
border-color: var(--cl-border-hover);
box-shadow: 0 4px 16px var(--cl-shadow);
}
.cl-faq__question {
@@ -1719,7 +1737,7 @@ html {
transition: transform 0.3s ease, border-color 0.2s;
}
.cl-faq__item[open] > .cl-faq__question::after {
.cl-faq__card[open] > .cl-faq__question::after {
transform: translateY(-30%) rotate(-135deg);
border-color: var(--cl-accent);
}
@@ -1930,7 +1948,6 @@ html {
color: var(--cl-muted);
font-size: 0.88rem;
line-height: 1.7;
max-width: 700px;
}
/* ═══════════════════════════════════════════════════════════════════