From 8ceebab2160672b37f2721d9ccadd58a1cc8742e Mon Sep 17 00:00:00 2001 From: yaosanqi137 Date: Tue, 1 Sep 2026 01:14:32 +0800 Subject: [PATCH] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=B9=B6=E6=8F=90=E5=8D=87=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/itstudio-recruitment/assets/otp.css | 51 +- plugins/itstudio-recruitment/assets/otp.js | 99 ++- .../includes/email-otp.php | 34 + .../ITStudioMainSite/assets/css/join-page.css | 767 +++++++++++------- .../ITStudioMainSite/assets/js/hero-waves.js | 30 +- .../ITStudioMainSite/assets/js/join-canvas.js | 61 +- .../assets/js/landing-hero-canvas.js | 44 +- 7 files changed, 737 insertions(+), 349 deletions(-) diff --git a/plugins/itstudio-recruitment/assets/otp.css b/plugins/itstudio-recruitment/assets/otp.css index f2fd4f3..4069138 100644 --- a/plugins/itstudio-recruitment/assets/otp.css +++ b/plugins/itstudio-recruitment/assets/otp.css @@ -5,9 +5,7 @@ .itstudio-otp-wrap { display: flex; flex-direction: column; - gap: 8px; width: 100%; - margin-top: 4px; } .itstudio-otp-group { @@ -69,9 +67,9 @@ } .itstudio-otp-send.is-countdown { - background: rgba(56, 139, 253, 0.12) !important; - border: 1px solid rgba(56, 139, 253, 0.3) !important; - color: #58a6ff !important; + background: #6b7280 !important; + border: 1px solid #7c8490 !important; + color: #e5e7eb !important; font-variant-numeric: tabular-nums; font-weight: 600; } @@ -82,28 +80,7 @@ } .itstudio-otp-status { - display: flex; - align-items: center; - gap: 6px; - font-size: 0.84rem; - line-height: 1.45; - padding: 2px 4px; - color: var(--text-secondary); - transition: all 0.2s ease; -} - -.itstudio-otp-status.is-success { - color: #3fb950; - font-weight: 500; -} - -.itstudio-otp-status.is-error { - color: #f85149; - font-weight: 500; -} - -.itstudio-otp-status.is-info { - color: #58a6ff; + width: 100%; } /* 浅色模式适配 */ @@ -118,21 +95,9 @@ } [data-theme="light"] .itstudio-otp-send.is-countdown { - background: rgba(9, 105, 218, 0.08) !important; - border: 1px solid rgba(9, 105, 218, 0.25) !important; - color: #0969da !important; -} - -[data-theme="light"] .itstudio-otp-status.is-success { - color: #1a7f37; -} - -[data-theme="light"] .itstudio-otp-status.is-error { - color: #cf222e; -} - -[data-theme="light"] .itstudio-otp-status.is-info { - color: #0969da; + background: #9ca3af !important; + border: 1px solid #9ca3af !important; + color: #ffffff !important; } @media (max-width: 480px) { @@ -142,4 +107,4 @@ .itstudio-otp-send { width: 100%; } -} \ No newline at end of file +} diff --git a/plugins/itstudio-recruitment/assets/otp.js b/plugins/itstudio-recruitment/assets/otp.js index 350753b..4c7d949 100644 --- a/plugins/itstudio-recruitment/assets/otp.js +++ b/plugins/itstudio-recruitment/assets/otp.js @@ -6,6 +6,8 @@ return; } + const OTP_COOLDOWN_SECONDS = 60; + function getLanguage() { return document.documentElement.lang === 'en' ? 'en' : 'cn'; } @@ -41,12 +43,18 @@ } status.style.display = 'flex'; status.textContent = message; - status.className = 'itstudio-otp-status' + (tone ? ' is-' + tone : ''); + status.className = 'itstudio-otp-status ' + (tone === 'error' ? 'frm_error_style' : 'frm_message'); } function startCooldown(button, seconds) { - let remaining = Math.max(1, Number(seconds) || Number(config.cooldown) || 60); + let remaining = Math.max(1, Number(seconds) || OTP_COOLDOWN_SECONDS); + if (button.itstudioOtpTimer) { + window.clearInterval(button.itstudioOtpTimer); + } + button.disabled = true; + button.dataset.otpLocked = '1'; + button.setAttribute('aria-disabled', 'true'); button.classList.add('is-countdown'); const update = function () { @@ -55,6 +63,8 @@ if (remaining < 0) { window.clearInterval(button.itstudioOtpTimer); button.itstudioOtpTimer = null; + button.dataset.otpLocked = '0'; + button.setAttribute('aria-disabled', 'false'); button.classList.remove('is-countdown'); button.textContent = config.labels.send || '发送验证码'; button.disabled = false; @@ -65,7 +75,36 @@ button.itstudioOtpTimer = window.setInterval(update, 1000); } + async function readAjaxResult(response) { + const raw = await response.text(); + const normalized = raw.replace(/^\uFEFF/, '').trim(); + if (!normalized) { + throw new Error('Empty AJAX response'); + } + + const result = JSON.parse(normalized); + if (result && typeof result === 'object' && typeof result.success !== 'undefined') { + return { + success: result.success === true || result.success === 1 || result.success === '1', + payload: result.data && typeof result.data === 'object' ? result.data : {} + }; + } + + if (result && typeof result === 'object' && (result.message_cn || result.message_en)) { + return { + success: true, + payload: result + }; + } + + throw new Error('Unexpected AJAX response'); + } + async function sendCode(form, button, status) { + if (button.dataset.otpLocked === '1' || button.disabled) { + return; + } + const emailInput = findField(form, config.emailFieldId); const email = emailInput ? emailInput.value.trim() : ''; if (!email) { @@ -79,7 +118,10 @@ return; } + // Block rapid duplicate clicks while the request is in flight. button.disabled = true; + button.dataset.otpLocked = '1'; + button.setAttribute('aria-disabled', 'true'); button.classList.add('is-loading'); button.textContent = config.labels.sending || '发送中…'; setStatus(status, config.labels.sending || '正在发送验证码…', 'info'); @@ -95,26 +137,26 @@ nonce: config.nonce }).toString() }); - const result = await response.json(); - const payload = result && result.data ? result.data : {}; + const result = await readAjaxResult(response); + const payload = result.payload || {}; button.classList.remove('is-loading'); if (!result.success) { setStatus(status, getMessage(payload, config.labels.networkError || '发送失败,请稍后重试。'), 'error'); - if (payload.wait) { - startCooldown(button, payload.wait); - } else { - button.textContent = config.labels.send || '发送验证码'; - button.disabled = false; - } + button.dataset.otpLocked = '0'; + button.setAttribute('aria-disabled', 'false'); + button.textContent = config.labels.send || '发送验证码'; + button.disabled = false; return; } setStatus(status, getMessage(payload, '验证码已发送,请检查邮箱及垃圾箱。'), 'success'); - startCooldown(button, payload.cooldown || config.cooldown); + startCooldown(button, payload.cooldown || OTP_COOLDOWN_SECONDS); } catch (error) { button.classList.remove('is-loading'); setStatus(status, config.labels.networkError || '网络错误,请稍后重试。', 'error'); + button.dataset.otpLocked = '0'; + button.setAttribute('aria-disabled', 'false'); button.textContent = config.labels.send || '发送验证码'; button.disabled = false; } @@ -150,6 +192,8 @@ button.type = 'button'; button.className = 'itstudio-otp-send'; button.textContent = config.labels.send || '发送验证码'; + button.dataset.otpLocked = '0'; + button.setAttribute('aria-disabled', 'false'); const status = document.createElement('div'); status.className = 'itstudio-otp-status'; @@ -171,7 +215,34 @@ }); } + let mountQueued = false; + const scheduleMount = function () { + if (mountQueued) { + return; + } + + mountQueued = true; + window.requestAnimationFrame(function () { + mountQueued = false; + mount(); + }); + }; + mount(); - const observer = new MutationObserver(mount); - observer.observe(document.body, {childList: true, subtree: true}); -})(); \ No newline at end of file + const observerRoot = document.querySelector('.join-form-content') || document.body; + const observer = new MutationObserver(function (mutations) { + const formStructureChanged = mutations.some(function (mutation) { + return Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)).some(function (node) { + return node.nodeType === 1 && ( + node.matches('.frm_forms, form') || + Boolean(node.querySelector && node.querySelector('.frm_forms, form')) + ); + }); + }); + + if (formStructureChanged) { + scheduleMount(); + } + }); + observer.observe(observerRoot, {childList: true, subtree: true}); +})(); diff --git a/plugins/itstudio-recruitment/includes/email-otp.php b/plugins/itstudio-recruitment/includes/email-otp.php index 643e1d4..0a44eca 100644 --- a/plugins/itstudio-recruitment/includes/email-otp.php +++ b/plugins/itstudio-recruitment/includes/email-otp.php @@ -57,6 +57,36 @@ function itstudio_join_otp_random_code() { return (string) wp_rand(100000, 999999); } +function itstudio_join_otp_email_exists_in_form($form_id, $field_id, $email) { + global $wpdb; + + $form_id = absint($form_id); + $field_id = absint($field_id); + $email = itstudio_join_otp_normalize_email($email); + if ($form_id <= 0 || $field_id <= 0 || $email === '') { + return false; + } + + $items_table = $wpdb->prefix . 'frm_items'; + $metas_table = $wpdb->prefix . 'frm_item_metas'; + $count = $wpdb->get_var( + $wpdb->prepare( + "SELECT COUNT(1) + FROM {$metas_table} AS meta + INNER JOIN {$items_table} AS item ON item.id = meta.item_id + WHERE item.form_id = %d + AND item.is_draft = 0 + AND meta.field_id = %d + AND LOWER(TRIM(meta.meta_value)) = %s", + $form_id, + $field_id, + $email + ) + ); + + return (int) $count > 0; +} + function itstudio_join_otp_json_error($message_cn, $message_en, $status = 400, $extra = array()) { wp_send_json_error( array_merge( @@ -85,6 +115,10 @@ function itstudio_join_otp_send_ajax() { itstudio_join_otp_json_error('请输入有效的邮箱地址。', 'Please enter a valid email address.', 400); } + if (itstudio_join_otp_email_exists_in_form($config['form_id'], $config['email_field_id'], $email)) { + itstudio_join_otp_json_error('该邮箱已经报名,无需重复提交。', 'This email has already been used to apply.', 409); + } + $ip = itstudio_join_otp_get_client_ip(); $otp_key = itstudio_join_otp_key($email, $config['form_id']); $existing = get_transient($otp_key); diff --git a/themes/ITStudioMainSite/assets/css/join-page.css b/themes/ITStudioMainSite/assets/css/join-page.css index 4bb1695..c4fbc53 100644 --- a/themes/ITStudioMainSite/assets/css/join-page.css +++ b/themes/ITStudioMainSite/assets/css/join-page.css @@ -1,4 +1,4 @@ -.join-page { +.join-page { padding: 36px 0 72px; } @@ -659,32 +659,30 @@ overflow-wrap: anywhere; } +/* ========================================= + 招新报名表单与 Formidable Forms 样式全面重构 + ========================================= */ + .join-form-card { position: relative; overflow: hidden; - border: 1px solid color-mix(in srgb, var(--color-primary) 28%, var(--border-default) 72%); - border-radius: 6px; + border: 1px solid color-mix(in srgb, var(--color-primary) 32%, var(--border-default) 68%); + border-radius: 12px; background: - linear-gradient( - 118deg, - color-mix(in srgb, var(--color-primary) 12%, transparent) 0%, - transparent 36% - ), radial-gradient( - 120% 80% at 100% 0%, - color-mix(in srgb, var(--color-primary) 14%, transparent) 0%, - transparent 52% + 120% 90% at 100% 0%, + color-mix(in srgb, var(--color-primary) 12%, transparent) 0%, + transparent 55% ), linear-gradient( 180deg, - color-mix(in srgb, var(--bg-card) 84%, #0f1a2b 16%) 0%, - color-mix(in srgb, var(--bg-card) 95%, transparent) 100% + color-mix(in srgb, var(--bg-card) 92%, #0d1117 8%) 0%, + color-mix(in srgb, var(--bg-card) 98%, transparent) 100% ); - padding: 18px; + padding: 24px; box-shadow: - 0 22px 40px -32px color-mix(in srgb, #000000 78%, transparent), - inset 0 1px 0 color-mix(in srgb, #9bc9ff 18%, transparent), - inset 0 -1px 0 color-mix(in srgb, #0a101a 70%, transparent); + 0 16px 36px -20px color-mix(in srgb, #000000 70%, transparent), + inset 0 1px 0 color-mix(in srgb, #9bc9ff 16%, transparent); } .join-form-card::before { @@ -695,32 +693,32 @@ background: repeating-linear-gradient( 0deg, - color-mix(in srgb, var(--color-primary) 8%, transparent) 0 1px, - transparent 1px 13px + color-mix(in srgb, var(--color-primary) 6%, transparent) 0 1px, + transparent 1px 16px ), repeating-linear-gradient( 90deg, - color-mix(in srgb, var(--color-primary) 7%, transparent) 0 1px, - transparent 1px 18px + color-mix(in srgb, var(--color-primary) 5%, transparent) 0 1px, + transparent 1px 20px ); - opacity: 0.26; + opacity: 0.22; } .join-form-card::after { content: ""; position: absolute; - inset: 10px; + inset: 12px; pointer-events: none; background: - linear-gradient(90deg, color-mix(in srgb, var(--color-primary) 72%, transparent), transparent) - top left / 88px 1px no-repeat, - linear-gradient(180deg, color-mix(in srgb, var(--color-primary) 72%, transparent), transparent) - top left / 1px 44px no-repeat, - linear-gradient(270deg, color-mix(in srgb, var(--color-primary) 48%, transparent), transparent) - bottom right / 66px 1px no-repeat, - linear-gradient(0deg, color-mix(in srgb, var(--color-primary) 48%, transparent), transparent) + linear-gradient(90deg, color-mix(in srgb, var(--color-primary) 70%, transparent), transparent) + top left / 80px 1px no-repeat, + linear-gradient(180deg, color-mix(in srgb, var(--color-primary) 70%, transparent), transparent) + top left / 1px 40px no-repeat, + linear-gradient(270deg, color-mix(in srgb, var(--color-primary) 50%, transparent), transparent) + bottom right / 60px 1px no-repeat, + linear-gradient(0deg, color-mix(in srgb, var(--color-primary) 50%, transparent), transparent) bottom right / 1px 30px no-repeat; - opacity: 0.65; + opacity: 0.6; } .join-form-card > * { @@ -729,7 +727,7 @@ } .join-form-card + .join-panel-title { - margin-top: 16px; + margin-top: 20px; } .join-form-card-placeholder { @@ -739,13 +737,14 @@ } .join-form-content { - margin-top: 14px; + margin-top: 10px; } .join-form-content form { margin: 0; } +/* --- 录取进度查询表单 --- */ .join-progress-query-form { display: grid; gap: 12px; @@ -775,7 +774,7 @@ border-radius: 8px; background: color-mix(in srgb, var(--bg-card) 90%, transparent) !important; color: var(--text-primary) !important; - padding: 9px 10px; + padding: 10px 12px; line-height: 1.3; transition: border-color var(--duration-fast), box-shadow var(--duration-fast), background-color var(--duration-fast); } @@ -791,10 +790,10 @@ .join-progress-query-field input:focus { outline: none; - border-color: color-mix(in srgb, var(--color-primary) 55%, var(--border-default) 45%) !important; - background: color-mix(in srgb, var(--bg-card) 90%, transparent) !important; + border-color: var(--color-primary) !important; + background: color-mix(in srgb, var(--bg-card) 95%, transparent) !important; color: var(--text-primary) !important; - box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 16%, transparent) !important; + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 22%, transparent) !important; } .join-progress-query-field input:-webkit-autofill, @@ -827,23 +826,25 @@ .join-progress-query-submit { border: 1px solid color-mix(in srgb, var(--color-primary) 48%, var(--border-default) 52%); - border-radius: 10px; + border-radius: 8px; background: color-mix(in srgb, var(--color-primary) 18%, var(--bg-card) 82%); color: var(--text-primary); - padding: 9px 16px; + padding: 10px 18px; font-size: 0.9rem; font-weight: 600; cursor: pointer; + transition: all 0.2s ease; } .join-progress-query-submit:hover { - background: color-mix(in srgb, var(--color-primary) 24%, var(--bg-card) 76%); + background: color-mix(in srgb, var(--color-primary) 28%, var(--bg-card) 72%); + transform: translateY(-1px); } .join-progress-query-feedback { - margin: 2px 0 0; - padding: 10px 12px; - border-radius: 10px; + margin: 8px 0 0; + padding: 10px 14px; + border-radius: 8px; border: 1px solid color-mix(in srgb, var(--border-default) 80%, transparent); background: color-mix(in srgb, var(--bg-card) 90%, transparent); color: var(--text-primary); @@ -871,296 +872,390 @@ background: color-mix(in srgb, #c34646 14%, var(--bg-card) 86%); } -.join-form-content .frm_forms { +/* --- Formidable 表单字段容器与排版 --- */ +.join-form-content .frm_forms, +.join-form-content .frm_form_fields { margin: 0; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_fields, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_submit { max-width: none; } -.join-form-content .frm_style_formidable-style.with_frm_style, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_fields, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_field, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_field_wrapper { - color: color-mix(in srgb, var(--text-primary) 95%, #ffffff 5%) !important; +.join-form-content .frm_fields_container { + display: flex; + flex-direction: column; + gap: 16px; } -.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_field { - margin-bottom: 14px !important; +.join-form-content .frm_form_field, +.join-form-content .form-field { + margin-bottom: 0 !important; + display: flex; + flex-direction: column; } -.join-form-content .frm_style_formidable-style.with_frm_style label { - color: color-mix(in srgb, var(--text-primary) 95%, #ffffff 5%) !important; - opacity: 1 !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_primary_label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_section_heading h3, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_inline_box label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_radio label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_checkbox label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_opt_container label { - color: color-mix(in srgb, var(--text-primary) 92%, #ffffff 8%) !important; +.join-form-content .frm_primary_label, +.join-form-content label.frm_primary_label, +.join-form-content .frm_section_heading h3, +.join-form-content .frm_inline_box label { + color: var(--text-primary) !important; + font-size: 0.92rem !important; font-weight: 600 !important; + line-height: 1.4 !important; + letter-spacing: 0.01em !important; + margin-bottom: 7px !important; + display: inline-flex !important; + align-items: center !important; + gap: 3px !important; opacity: 1 !important; - letter-spacing: 0.01em; } -.join-form-content .frm_style_formidable-style.with_frm_style .frm_required { - color: #ff6767 !important; +.join-form-content .frm_required { + color: #f85149 !important; + font-weight: 700 !important; + font-size: 1em !important; + margin-left: 2px !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .frm_description, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_error, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_error_style { - color: color-mix(in srgb, var(--text-primary) 82%, var(--text-secondary) 18%) !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_error_style, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_error { - border-left: 3px solid #ff6767 !important; - background: color-mix(in srgb, #ff6767 10%, transparent) !important; - padding: 8px 10px !important; - border-radius: 4px; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_message, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_success_style, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_success { - border-left: 3px solid #42b983 !important; - background: color-mix(in srgb, #42b983 14%, transparent) !important; - color: color-mix(in srgb, var(--text-primary) 94%, #ffffff 6%) !important; - border-radius: 4px; - padding: 10px 12px !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style input[type="text"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="email"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="number"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="tel"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="date"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="datetime-local"], -.join-form-content .frm_style_formidable-style.with_frm_style textarea, -.join-form-content .frm_style_formidable-style.with_frm_style select { - background: color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) !important; - border: 1px solid color-mix(in srgb, var(--border-default) 42%, var(--color-primary) 58%) !important; - color: color-mix(in srgb, var(--text-primary) 96%, #ffffff 4%) !important; - min-height: 42px; +/* --- 顶部全局错误/提示横幅 (Modern Alert Banner) --- */ +.join-form-content .frm_error_style, +.join-form-content .frm_message.frm_error_style, +.join-form-content div.frm_error_style, +.join-form-content .frm_error { + position: relative !important; + display: flex !important; + align-items: center !important; + gap: 10px !important; + padding: 12px 16px 12px 42px !important; border-radius: 8px !important; - padding: 10px 12px !important; + border: 1px solid rgba(248, 81, 73, 0.4) !important; + border-left: 4px solid #f85149 !important; + background: linear-gradient(135deg, rgba(248, 81, 73, 0.16) 0%, rgba(248, 81, 73, 0.06) 100%) !important; + color: #ff7b72 !important; + font-size: 0.9rem !important; + font-weight: 500 !important; + line-height: 1.45 !important; + margin: 8px 0 18px 0 !important; + box-shadow: 0 4px 12px rgba(248, 81, 73, 0.1) !important; } -.join-form-content .frm_style_formidable-style.with_frm_style input::placeholder, -.join-form-content .frm_style_formidable-style.with_frm_style textarea::placeholder { - color: color-mix(in srgb, var(--text-secondary) 78%, var(--text-primary) 22%) !important; - opacity: 1; +.join-form-content .frm_error_style::before, +.join-form-content .frm_message.frm_error_style::before, +.join-form-content .frm_error::before { + content: "" !important; + position: absolute !important; + left: 14px !important; + top: 50% !important; + transform: translateY(-50%) !important; + width: 18px !important; + height: 18px !important; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f85149' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-size: contain !important; } -.join-form-content .frm_style_formidable-style.with_frm_style input[type="text"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style input[type="email"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style input[type="number"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style input[type="tel"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style input[type="date"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style input[type="datetime-local"]:focus, -.join-form-content .frm_style_formidable-style.with_frm_style textarea:focus, -.join-form-content .frm_style_formidable-style.with_frm_style select:focus { - border-color: color-mix(in srgb, var(--color-primary) 76%, #8ec9ff 24%) !important; - box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 24%, transparent) !important; +/* --- 成功提示 (Success Message) --- */ +.join-form-content .frm_message, +.join-form-content .frm_success_style, +.join-form-content .frm_success { + position: relative !important; + display: flex !important; + align-items: center !important; + gap: 10px !important; + padding: 12px 16px 12px 42px !important; + border-radius: 8px !important; + border: 1px solid rgba(63, 185, 80, 0.4) !important; + border-left: 4px solid #3fb950 !important; + background: linear-gradient(135deg, rgba(63, 185, 80, 0.16) 0%, rgba(63, 185, 80, 0.06) 100%) !important; + color: #7ee787 !important; + font-size: 0.9rem !important; + font-weight: 500 !important; + line-height: 1.45 !important; + margin-bottom: 18px !important; +} + +.join-form-content .frm_message::before, +.join-form-content .frm_success_style::before, +.join-form-content .frm_success::before { + content: "" !important; + position: absolute !important; + left: 14px !important; + top: 50% !important; + transform: translateY(-50%) !important; + width: 18px !important; + height: 18px !important; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233fb950' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 11.08V12a10 10 0 1 1-5.93-9.14'%3E%3C/path%3E%3Cpolyline points='22 4 12 14.01 9 11.01'%3E%3C/polyline%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-size: contain !important; +} + +.join-form-content .frm_description { + color: var(--text-secondary) !important; + font-size: 0.82rem !important; + line-height: 1.45 !important; + margin-top: 4px !important; +} + +/* --- 输入框统一质感 (Text, Email, Tel, Number, Select, Textarea) --- */ +.join-form-content input[type="text"], +.join-form-content input[type="email"], +.join-form-content input[type="number"], +.join-form-content input[type="tel"], +.join-form-content input[type="password"], +.join-form-content input[type="date"], +.join-form-content input[type="datetime-local"], +.join-form-content textarea, +.join-form-content select { + display: block !important; + width: 100% !important; + min-height: 44px !important; + padding: 10px 14px !important; + font-size: 0.94rem !important; + line-height: 1.4 !important; + font-family: inherit !important; + color: #f0f6fc !important; + background: rgba(13, 17, 23, 0.72) !important; + border: 1px solid rgba(240, 246, 252, 0.14) !important; + border-radius: 8px !important; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25) !important; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; + box-sizing: border-box !important; +} + +.join-form-content input[type="text"]:hover, +.join-form-content input[type="email"]:hover, +.join-form-content input[type="number"]:hover, +.join-form-content input[type="tel"]:hover, +.join-form-content input[type="password"]:hover, +.join-form-content input[type="date"]:hover, +.join-form-content input[type="datetime-local"]:hover, +.join-form-content textarea:hover, +.join-form-content select:hover { + border-color: rgba(88, 166, 255, 0.45) !important; + background: rgba(22, 27, 34, 0.85) !important; +} + +.join-form-content input[type="text"]:focus, +.join-form-content input[type="email"]:focus, +.join-form-content input[type="number"]:focus, +.join-form-content input[type="tel"]:focus, +.join-form-content input[type="password"]:focus, +.join-form-content input[type="date"]:focus, +.join-form-content input[type="datetime-local"]:focus, +.join-form-content textarea:focus, +.join-form-content select:focus { + border-color: #58a6ff !important; + background: rgba(22, 27, 34, 0.95) !important; + color: #ffffff !important; + box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.28), inset 0 1px 2px rgba(0, 0, 0, 0.2) !important; outline: none !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .iti { +.join-form-content input::placeholder, +.join-form-content textarea::placeholder { + color: #6d737c !important; + opacity: 1 !important; +} + +.join-form-content textarea { + min-height: 96px !important; + resize: vertical; +} + +/* --- 自动填充处理 --- */ +.join-form-content input:-webkit-autofill, +.join-form-content input:-webkit-autofill:hover, +.join-form-content input:-webkit-autofill:focus, +.join-form-content textarea:-webkit-autofill, +.join-form-content textarea:-webkit-autofill:hover, +.join-form-content textarea:-webkit-autofill:focus, +.join-form-content select:-webkit-autofill, +.join-form-content select:-webkit-autofill:hover, +.join-form-content select:-webkit-autofill:focus { + -webkit-text-fill-color: #f0f6fc !important; + -webkit-box-shadow: 0 0 0 1000px #161b22 inset !important; + box-shadow: 0 0 0 1000px #161b22 inset !important; + caret-color: #f0f6fc !important; + transition: background-color 9999s ease-out 0s !important; +} + +/* --- 国际电话组件 (iti) --- */ +.join-form-content .iti { width: 100% !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .iti input[type="tel"] { +.join-form-content .iti input[type="tel"] { padding-right: 12px !important; padding-left: calc(var(--iti-selected-country-arrow-padding, 64px) + 4px) !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .iti__selected-country { +.join-form-content .iti__selected-country { border-top-left-radius: 8px; border-bottom-left-radius: 8px; } -.join-form-content .frm_style_formidable-style.with_frm_style input:-webkit-autofill, -.join-form-content .frm_style_formidable-style.with_frm_style input:-webkit-autofill:hover, -.join-form-content .frm_style_formidable-style.with_frm_style input:-webkit-autofill:focus, -.join-form-content .frm_style_formidable-style.with_frm_style textarea:-webkit-autofill, -.join-form-content .frm_style_formidable-style.with_frm_style textarea:-webkit-autofill:hover, -.join-form-content .frm_style_formidable-style.with_frm_style textarea:-webkit-autofill:focus, -.join-form-content .frm_style_formidable-style.with_frm_style select:-webkit-autofill, -.join-form-content .frm_style_formidable-style.with_frm_style select:-webkit-autofill:hover, -.join-form-content .frm_style_formidable-style.with_frm_style select:-webkit-autofill:focus { - -webkit-text-fill-color: color-mix(in srgb, var(--text-primary) 96%, #ffffff 4%) !important; - -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) inset !important; - box-shadow: 0 0 0 1000px color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) inset !important; - caret-color: color-mix(in srgb, var(--text-primary) 96%, #ffffff 4%) !important; - transition: background-color 9999s ease-out 0s !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__dropdown-content { - background: color-mix(in srgb, var(--bg-card) 94%, var(--bg-body) 6%) !important; - border: 1px solid color-mix(in srgb, var(--border-default) 46%, var(--color-primary) 54%) !important; - color: color-mix(in srgb, var(--text-primary) 92%, #ffffff 8%) !important; - box-shadow: 0 18px 36px -22px color-mix(in srgb, #000000 68%, transparent); -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__search-input-wrapper { - margin: 6px 8px !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__search-input { - border: 1px solid color-mix(in srgb, var(--border-default) 38%, var(--color-primary) 62%) !important; - background: color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) !important; - color: color-mix(in srgb, var(--text-primary) 94%, #ffffff 6%) !important; - min-height: 40px !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__search-input::placeholder { - color: color-mix(in srgb, var(--text-secondary) 76%, var(--text-primary) 24%) !important; - opacity: 1; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__country-list { - background: transparent !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__country { - color: color-mix(in srgb, var(--text-primary) 90%, #ffffff 10%) !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__dial-code, -.join-form-content .frm_style_formidable-style.with_frm_style .iti__country-name { - color: color-mix(in srgb, var(--text-primary) 88%, var(--text-secondary) 12%) !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__country.iti__highlight, -.join-form-content .frm_style_formidable-style.with_frm_style .iti__country:hover { - background: color-mix(in srgb, var(--color-primary) 16%, var(--bg-card) 84%) !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .iti__divider { - border-bottom-color: color-mix(in srgb, var(--border-default) 70%, transparent) !important; -} - +.join-form-content .iti__dropdown-content, .frm_forms.with_frm_style .iti__dropdown-content { - background: color-mix(in srgb, var(--bg-card) 94%, var(--bg-body) 6%) !important; - border: 1px solid color-mix(in srgb, var(--border-default) 46%, var(--color-primary) 54%) !important; - color: color-mix(in srgb, var(--text-primary) 92%, #ffffff 8%) !important; - box-shadow: 0 18px 36px -22px color-mix(in srgb, #000000 68%, transparent); -} - -.frm_forms.with_frm_style .iti__search-input-wrapper { - margin: 6px 8px !important; + background: #161b22 !important; + border: 1px solid rgba(56, 139, 253, 0.4) !important; + color: #f0f6fc !important; + box-shadow: 0 18px 36px -16px rgba(0, 0, 0, 0.75); + border-radius: 8px; } +.join-form-content .iti__search-input, .frm_forms.with_frm_style .iti__search-input { - border: 1px solid color-mix(in srgb, var(--border-default) 38%, var(--color-primary) 62%) !important; - background: color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) !important; - color: color-mix(in srgb, var(--text-primary) 94%, #ffffff 6%) !important; - min-height: 40px !important; -} - -.frm_forms.with_frm_style .iti__search-input::placeholder { - color: color-mix(in srgb, var(--text-secondary) 76%, var(--text-primary) 24%) !important; - opacity: 1; -} - -.frm_forms.with_frm_style .iti__country-list { - background: transparent !important; + border: 1px solid rgba(240, 246, 252, 0.2) !important; + background: #0d1117 !important; + color: #f0f6fc !important; + min-height: 38px !important; + border-radius: 6px; } +.join-form-content .iti__country, .frm_forms.with_frm_style .iti__country { - color: color-mix(in srgb, var(--text-primary) 90%, #ffffff 10%) !important; -} - -.frm_forms.with_frm_style .iti__dial-code, -.frm_forms.with_frm_style .iti__country-name { - color: color-mix(in srgb, var(--text-primary) 88%, var(--text-secondary) 12%) !important; + color: #c9d1d9 !important; } +.join-form-content .iti__country.iti__highlight, +.join-form-content .iti__country:hover, .frm_forms.with_frm_style .iti__country.iti__highlight, .frm_forms.with_frm_style .iti__country:hover { - background: color-mix(in srgb, var(--color-primary) 16%, var(--bg-card) 84%) !important; + background: rgba(56, 139, 253, 0.2) !important; + color: #ffffff !important; } +.join-form-content .iti__divider, .frm_forms.with_frm_style .iti__divider { - border-bottom-color: color-mix(in srgb, var(--border-default) 70%, transparent) !important; + border-bottom-color: rgba(240, 246, 252, 0.1) !important; } -.join-form-content .frm_style_formidable-style.with_frm_style input[type="checkbox"], -.join-form-content .frm_style_formidable-style.with_frm_style input[type="radio"] { - accent-color: var(--color-primary); - transform: scale(1.06); -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_checkbox label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_radio label, -.join-form-content .frm_style_formidable-style.with_frm_style .frm_opt_container label { - line-height: 1.55 !important; - font-size: 0.96rem !important; -} - -.join-form-content .frm_style_formidable-style.with_frm_style .frm_submit { +/* --- 选项卡片化重构 (Checkbox & Radio Chips) --- */ +.join-form-content .frm_opt_container { display: flex !important; - flex-wrap: wrap; - gap: 10px; - align-items: center; + flex-wrap: wrap !important; + gap: 8px 12px !important; + margin-top: 6px !important; } -.join-form-content .frm_style_formidable-style.with_frm_style input[type="submit"], -.join-form-content .frm_style_formidable-style.with_frm_style button.frm_button_submit { - border-radius: 10px; - border: 1px solid color-mix(in srgb, var(--color-primary) 82%, #9cd3ff 18%) !important; - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--color-primary) 82%, #78c3ff 18%) 0%, - color-mix(in srgb, var(--color-primary) 92%, #5ea7f8 8%) 100% - ) !important; +.join-form-content .frm_checkbox, +.join-form-content .frm_radio { + margin: 0 !important; + display: inline-flex !important; + align-items: center !important; +} + +.join-form-content .frm_checkbox label, +.join-form-content .frm_radio label { + display: inline-flex !important; + align-items: center !important; + gap: 8px !important; + padding: 8px 14px !important; + border-radius: 8px !important; + border: 1px solid rgba(240, 246, 252, 0.12) !important; + background: rgba(22, 27, 34, 0.6) !important; + color: var(--text-primary) !important; + font-size: 0.92rem !important; + font-weight: 500 !important; + line-height: 1.4 !important; + cursor: pointer !important; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; + user-select: none !important; +} + +.join-form-content .frm_checkbox label:hover, +.join-form-content .frm_radio label:hover { + border-color: rgba(88, 166, 255, 0.45) !important; + background: rgba(56, 139, 253, 0.1) !important; + transform: translateY(-1px); +} + +.join-form-content .frm_checkbox input[type="checkbox"], +.join-form-content .frm_radio input[type="radio"] { + width: 16px !important; + min-height: auto !important; + height: 16px !important; + margin: 0 !important; + padding: 0 !important; + accent-color: var(--color-primary) !important; + cursor: pointer !important; +} + +/* --- 提交与操作按钮 (Submit & Actions) --- */ +.join-form-content .frm_submit { + display: flex !important; + flex-wrap: wrap !important; + gap: 12px !important; + align-items: center !important; + margin-top: 24px !important; + padding-top: 14px !important; + border-top: 1px solid rgba(240, 246, 252, 0.08) !important; + max-width: none !important; +} + +.join-form-content input[type="submit"], +.join-form-content button.frm_button_submit { + min-height: 46px !important; + padding: 0 28px !important; + border-radius: 8px !important; + border: 1px solid rgba(255, 255, 255, 0.16) !important; + background: linear-gradient(135deg, #1f6feb 0%, #1158c7 100%) !important; color: #ffffff !important; - font-weight: 700 !important; - letter-spacing: 0.01em; - min-height: 44px; - padding: 0 20px !important; - box-shadow: 0 8px 18px -12px color-mix(in srgb, var(--color-primary) 65%, transparent); + font-size: 0.95rem !important; + font-weight: 600 !important; + letter-spacing: 0.02em !important; + cursor: pointer !important; + box-shadow: 0 3px 12px rgba(31, 111, 235, 0.35) !important; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; + user-select: none !important; } -.join-form-content .frm_style_formidable-style.with_frm_style input[type="submit"]:hover, -.join-form-content .frm_style_formidable-style.with_frm_style button.frm_button_submit:hover { - filter: brightness(1.05); +.join-form-content input[type="submit"]:hover, +.join-form-content button.frm_button_submit:hover { + transform: translateY(-1px) !important; + filter: brightness(1.08) !important; + box-shadow: 0 6px 18px rgba(31, 111, 235, 0.45) !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .frm_save_draft, -.join-form-content .frm_style_formidable-style.with_frm_style button[name*="draft"], -.join-form-content .frm_style_formidable-style.with_frm_style input[name*="draft"], -.join-form-content .frm_style_formidable-style.with_frm_style button[value*="草稿"], -.join-form-content .frm_style_formidable-style.with_frm_style input[value*="草稿"], -.join-form-content .frm_style_formidable-style.with_frm_style button[value*="Draft"], -.join-form-content .frm_style_formidable-style.with_frm_style input[value*="Draft"] { - border: 1px solid color-mix(in srgb, var(--border-default) 30%, var(--color-primary) 70%) !important; - background: color-mix(in srgb, var(--bg-card) 88%, transparent) !important; - color: var(--color-primary) !important; +.join-form-content input[type="submit"]:active, +.join-form-content button.frm_button_submit:active { + transform: translateY(0) !important; + filter: brightness(0.96) !important; +} + +.join-form-content .frm_save_draft, +.join-form-content button[name*="draft"], +.join-form-content input[name*="draft"], +.join-form-content button[value*="草稿"], +.join-form-content input[value*="草稿"], +.join-form-content button[value*="Draft"], +.join-form-content input[value*="Draft"] { + min-height: 46px !important; + padding: 0 22px !important; + border-radius: 8px !important; + border: 1px solid rgba(240, 246, 252, 0.16) !important; + background: rgba(22, 27, 34, 0.6) !important; + color: var(--text-primary) !important; + font-size: 0.92rem !important; + font-weight: 500 !important; + cursor: pointer !important; box-shadow: none !important; + transition: all 0.2s ease !important; } -.join-form-content .frm_style_formidable-style.with_frm_style .frm_save_draft:hover, -.join-form-content .frm_style_formidable-style.with_frm_style button[name*="draft"]:hover, -.join-form-content .frm_style_formidable-style.with_frm_style input[name*="draft"]:hover { - background: color-mix(in srgb, var(--bg-card) 74%, var(--color-primary) 26%) !important; - color: #ffffff !important; +.join-form-content .frm_save_draft:hover, +.join-form-content button[name*="draft"]:hover, +.join-form-content input[name*="draft"]:hover { + border-color: var(--color-primary) !important; + background: rgba(56, 139, 253, 0.1) !important; + color: var(--color-primary) !important; } +/* ========================================= + 浅色模式适配 (Light Theme) + ========================================= */ + [data-theme="light"] .join-form-card { border-color: color-mix(in srgb, #8bbcff 52%, var(--border-default) 48%); background: - linear-gradient( - 118deg, - color-mix(in srgb, #d9eaff 42%, transparent) 0%, - transparent 40% - ), radial-gradient( 140% 90% at 100% 0%, color-mix(in srgb, #dbe9ff 48%, transparent) 0%, @@ -1176,16 +1271,134 @@ inset 0 1px 0 rgba(255, 255, 255, 0.7); } -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="text"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="email"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="number"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="tel"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="date"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="datetime-local"], -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style textarea, -[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style select { +[data-theme="light"] .join-form-content input[type="text"], +[data-theme="light"] .join-form-content input[type="email"], +[data-theme="light"] .join-form-content input[type="number"], +[data-theme="light"] .join-form-content input[type="tel"], +[data-theme="light"] .join-form-content input[type="password"], +[data-theme="light"] .join-form-content input[type="date"], +[data-theme="light"] .join-form-content input[type="datetime-local"], +[data-theme="light"] .join-form-content textarea, +[data-theme="light"] .join-form-content select { background: #ffffff !important; - border-color: color-mix(in srgb, #8bbcff 46%, var(--border-default) 54%) !important; + border: 1px solid #d0d7de !important; + color: #1f2328 !important; + box-shadow: inset 0 1px 2px rgba(31, 35, 40, 0.04) !important; +} + +[data-theme="light"] .join-form-content input[type="text"]:hover, +[data-theme="light"] .join-form-content input[type="email"]:hover, +[data-theme="light"] .join-form-content input[type="number"]:hover, +[data-theme="light"] .join-form-content input[type="tel"]:hover, +[data-theme="light"] .join-form-content input[type="password"]:hover, +[data-theme="light"] .join-form-content input[type="date"]:hover, +[data-theme="light"] .join-form-content input[type="datetime-local"]:hover, +[data-theme="light"] .join-form-content textarea:hover, +[data-theme="light"] .join-form-content select:hover { + border-color: #0969da !important; +} + +[data-theme="light"] .join-form-content input[type="text"]:focus, +[data-theme="light"] .join-form-content input[type="email"]:focus, +[data-theme="light"] .join-form-content input[type="number"]:focus, +[data-theme="light"] .join-form-content input[type="tel"]:focus, +[data-theme="light"] .join-form-content input[type="password"]:focus, +[data-theme="light"] .join-form-content input[type="date"]:focus, +[data-theme="light"] .join-form-content input[type="datetime-local"]:focus, +[data-theme="light"] .join-form-content textarea:focus, +[data-theme="light"] .join-form-content select:focus { + border-color: #0969da !important; + background: #ffffff !important; + color: #1f2328 !important; + box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.2), inset 0 1px 2px rgba(31, 35, 40, 0.04) !important; +} + +[data-theme="light"] .join-form-content input::placeholder, +[data-theme="light"] .join-form-content textarea::placeholder { + color: #8c959f !important; +} + +[data-theme="light"] .join-form-content input:-webkit-autofill, +[data-theme="light"] .join-form-content input:-webkit-autofill:hover, +[data-theme="light"] .join-form-content input:-webkit-autofill:focus, +[data-theme="light"] .join-form-content textarea:-webkit-autofill, +[data-theme="light"] .join-form-content textarea:-webkit-autofill:hover, +[data-theme="light"] .join-form-content textarea:-webkit-autofill:focus, +[data-theme="light"] .join-form-content select:-webkit-autofill, +[data-theme="light"] .join-form-content select:-webkit-autofill:hover, +[data-theme="light"] .join-form-content select:-webkit-autofill:focus { + -webkit-text-fill-color: #1f2328 !important; + -webkit-box-shadow: 0 0 0 1000px #ffffff inset !important; + box-shadow: 0 0 0 1000px #ffffff inset !important; +} + +[data-theme="light"] .join-form-content .frm_error_style, +[data-theme="light"] .join-form-content .frm_message.frm_error_style, +[data-theme="light"] .join-form-content .frm_error { + background: #ffebe9 !important; + border: 1px solid rgba(207, 34, 46, 0.3) !important; + border-left: 4px solid #cf222e !important; + color: #a40e26 !important; +} + +[data-theme="light"] .join-form-content .frm_error_style::before, +[data-theme="light"] .join-form-content .frm_message.frm_error_style::before, +[data-theme="light"] .join-form-content .frm_error::before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23cf222e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E") !important; +} + +[data-theme="light"] .join-form-content .frm_message, +[data-theme="light"] .join-form-content .frm_success_style, +[data-theme="light"] .join-form-content .frm_success { + background: #dafbe1 !important; + border: 1px solid rgba(31, 136, 61, 0.3) !important; + border-left: 4px solid #1f883d !important; + color: #1a7f37 !important; +} + +[data-theme="light"] .join-form-content .frm_message::before, +[data-theme="light"] .join-form-content .frm_success_style::before, +[data-theme="light"] .join-form-content .frm_success::before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a7f37' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 11.08V12a10 10 0 1 1-5.93-9.14'%3E%3C/path%3E%3Cpolyline points='22 4 12 14.01 9 11.01'%3E%3C/polyline%3E%3C/svg%3E") !important; +} + +[data-theme="light"] .join-form-content .frm_checkbox label, +[data-theme="light"] .join-form-content .frm_radio label { + border: 1px solid #d0d7de !important; + background: #f6f8fa !important; +} + +[data-theme="light"] .join-form-content .frm_checkbox label:hover, +[data-theme="light"] .join-form-content .frm_radio label:hover { + border-color: #0969da !important; + background: rgba(9, 105, 218, 0.06) !important; +} + +[data-theme="light"] .join-form-content input[type="submit"], +[data-theme="light"] .join-form-content button.frm_button_submit { + background: linear-gradient(135deg, #0969da 0%, #0550ae 100%) !important; + box-shadow: 0 3px 12px rgba(9, 105, 218, 0.28) !important; +} + +[data-theme="light"] .join-form-content input[type="submit"]:hover, +[data-theme="light"] .join-form-content button.frm_button_submit:hover { + box-shadow: 0 6px 18px rgba(9, 105, 218, 0.38) !important; +} + +[data-theme="light"] .join-form-content .frm_save_draft, +[data-theme="light"] .join-form-content button[name*="draft"], +[data-theme="light"] .join-form-content input[name*="draft"] { + border: 1px solid #d0d7de !important; + background: #f6f8fa !important; + color: #1f2328 !important; +} + +[data-theme="light"] .join-form-content .frm_save_draft:hover, +[data-theme="light"] .join-form-content button[name*="draft"]:hover, +[data-theme="light"] .join-form-content input[name*="draft"]:hover { + border-color: #0969da !important; + background: rgba(9, 105, 218, 0.06) !important; + color: #0969da !important; } .join-form-tip { diff --git a/themes/ITStudioMainSite/assets/js/hero-waves.js b/themes/ITStudioMainSite/assets/js/hero-waves.js index a2f53d4..19b34fe 100644 --- a/themes/ITStudioMainSite/assets/js/hero-waves.js +++ b/themes/ITStudioMainSite/assets/js/hero-waves.js @@ -13,6 +13,8 @@ let height = 0; let dpr = 1; let rafId = null; + let pageVisible = document.visibilityState !== 'hidden'; + let heroVisible = true; const waveDefs = [ { yRatio: 0.3, amplitude: 24, wavelength: 940, speed: 0.5, alpha: 0.98 }, @@ -45,7 +47,7 @@ }; const resize = () => { - dpr = window.devicePixelRatio || 1; + dpr = Math.min(window.devicePixelRatio || 1, 2); width = main.clientWidth; height = Math.max(1, Math.round(getCoverageHeight())); canvas.width = Math.floor(width * dpr); @@ -86,6 +88,11 @@ }; const render = (timestamp) => { + if (!pageVisible || !heroVisible || prefersReduced.matches) { + rafId = null; + return; + } + const palette = palettes[getTheme()]; const time = timestamp * 0.001; ctx.clearRect(0, 0, width, height); @@ -111,7 +118,7 @@ const start = () => { stop(); resize(); - if (prefersReduced.matches) { + if (prefersReduced.matches || !pageVisible || !heroVisible) { drawStatic(); return; } @@ -136,5 +143,24 @@ window.addEventListener('resize', onResize); window.addEventListener('load', onResize); + const updateVisibility = () => { + pageVisible = document.visibilityState !== 'hidden'; + if (pageVisible && heroVisible) { + start(); + } else { + stop(); + } + }; + + document.addEventListener('visibilitychange', updateVisibility); + + if (typeof IntersectionObserver !== 'undefined') { + const visibilityObserver = new IntersectionObserver((entries) => { + heroVisible = entries.length === 0 || entries[0].isIntersecting; + updateVisibility(); + }, { threshold: 0.01 }); + visibilityObserver.observe(canvas); + } + start(); })(); diff --git a/themes/ITStudioMainSite/assets/js/join-canvas.js b/themes/ITStudioMainSite/assets/js/join-canvas.js index 2ce7976..1638de0 100644 --- a/themes/ITStudioMainSite/assets/js/join-canvas.js +++ b/themes/ITStudioMainSite/assets/js/join-canvas.js @@ -34,6 +34,8 @@ let trackWidth = 0; let stageMarkers = []; let stageLayout = null; + let pageVisible = document.visibilityState !== 'hidden'; + let canvasVisible = true; const waveShape1 = { base: 0.16, amp: 7.5, len: 280, speed: 0.9 }; const waveShape2 = { base: 0.44, amp: 6, len: 340, speed: 0.75 }; @@ -166,7 +168,7 @@ } function resize() { - dpr = window.devicePixelRatio || 1; + dpr = Math.min(window.devicePixelRatio || 1, 2); width = canvas.clientWidth; height = canvas.clientHeight; canvas.width = Math.max(1, Math.floor(width * dpr)); @@ -245,6 +247,11 @@ } function renderWaves(timestamp) { + if (!pageVisible || !canvasVisible || prefersReducedMotion.matches) { + rafId = 0; + return; + } + const theme = getTheme(); const palette = palettes[theme]; const time = timestamp * 0.001; @@ -279,7 +286,7 @@ function startWaves() { stopWaves(); resize(); - if (prefersReducedMotion.matches) { + if (prefersReducedMotion.matches || !pageVisible || !canvasVisible) { drawStaticWaves(); return; } @@ -604,6 +611,25 @@ }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); + const updateVisibility = () => { + pageVisible = document.visibilityState !== 'hidden'; + if (pageVisible && canvasVisible) { + startWaves(); + } else { + stopWaves(); + } + }; + + document.addEventListener('visibilitychange', updateVisibility); + + if (typeof IntersectionObserver !== 'undefined') { + const visibilityObserver = new IntersectionObserver((entries) => { + canvasVisible = entries.length === 0 || entries[0].isIntersecting; + updateVisibility(); + }, { threshold: 0.01 }); + visibilityObserver.observe(canvas); + } + if (typeof prefersReducedMotion.addEventListener === 'function') { prefersReducedMotion.addEventListener('change', handleMotionPreferenceChange); } else if (typeof prefersReducedMotion.addListener === 'function') { @@ -830,8 +856,9 @@ return; } + const successSelector = '.frm_message:not(.itstudio-otp-status), .frm_success_style, .frm_success'; const updateSuccessMessage = () => { - const successNode = formHost.querySelector('.frm_message, .frm_success_style, .frm_success'); + const successNode = formHost.querySelector(successSelector); if (!successNode) { return; } @@ -843,13 +870,37 @@ const messageCn = resolvedIntent === 'draft' ? '草稿已保存' : '你的报名表单已提交,感谢报名'; const messageEn = resolvedIntent === 'draft' ? 'Draft saved.' : 'Your registration form has been submitted. Thank you for applying.'; + const nextText = pickLocaleText(messageCn, messageEn); + if (successNode.dataset.itstudioNoticeIntent === resolvedIntent && successNode.textContent === nextText) { + return; + } + + successNode.dataset.itstudioNoticeIntent = resolvedIntent; successNode.dataset.cn = messageCn; successNode.dataset.en = messageEn; - successNode.textContent = pickLocaleText(messageCn, messageEn); + successNode.textContent = nextText; showJoinSubmitNotice(resolvedIntent); }; - const observer = new MutationObserver(updateSuccessMessage); + const observer = new MutationObserver((mutations) => { + const successChanged = mutations.some((mutation) => { + const target = mutation.target && mutation.target.nodeType === 1 ? mutation.target : null; + if (target && (target.matches(successSelector) || target.closest(successSelector))) { + return true; + } + + return Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)).some((node) => { + return node.nodeType === 1 && ( + node.matches(successSelector) || + Boolean(node.querySelector && node.querySelector(successSelector)) + ); + }); + }); + + if (successChanged) { + updateSuccessMessage(); + } + }); observer.observe(formHost, { childList: true, subtree: true }); } diff --git a/themes/ITStudioMainSite/assets/js/landing-hero-canvas.js b/themes/ITStudioMainSite/assets/js/landing-hero-canvas.js index 68e3a51..0f11447 100644 --- a/themes/ITStudioMainSite/assets/js/landing-hero-canvas.js +++ b/themes/ITStudioMainSite/assets/js/landing-hero-canvas.js @@ -13,6 +13,8 @@ let dpr = 1; let rafId = null; let startAt = performance.now(); + let pageVisible = document.visibilityState !== 'hidden'; + let heroVisible = true; const EMPTY_SET = new Set(); const WORD_RE = /^[A-Za-z_][A-Za-z0-9_]*$/; @@ -615,12 +617,16 @@ const clamp = (value, min, max) => Math.max(min, Math.min(max, value)); const getTheme = () => (document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'); const tokenCount = (tokens) => tokens.reduce((sum, token) => sum + token[0].length, 0); + const lineCharTotals = codeLines.map((line) => tokenCount(line)); + const lineDurations = lineCharTotals.map((chars) => Math.max(150, chars * 8)); + const totalTypingDuration = lineDurations.reduce((sum, duration) => sum + duration, 0); + const cycleDuration = totalTypingDuration + 500; const resizeCanvas = () => { const rect = hero.getBoundingClientRect(); width = Math.max(1, Math.round(rect.width)); height = Math.max(1, Math.round(rect.height)); - dpr = window.devicePixelRatio || 1; + dpr = Math.min(window.devicePixelRatio || 1, 2); canvas.width = Math.floor(width * dpr); canvas.height = Math.floor(height * dpr); canvas.style.width = `${width}px`; @@ -668,12 +674,6 @@ const baseLineNo = 1; const scrollLeadLines = 2; - const lineCharTotals = codeLines.map((line) => tokenCount(line)); - const lineDurations = lineCharTotals.map((chars) => Math.max(150, chars * 8)); - const totalTypingDuration = lineDurations.reduce((sum, duration) => sum + duration, 0); - const holdDuration = 500; - const cycleDuration = totalTypingDuration + holdDuration; - ctx.fillStyle = palette.background; ctx.fillRect(0, 0, width, height); ctx.fillStyle = palette.editor; @@ -683,7 +683,7 @@ ctx.fillStyle = palette.divider; ctx.fillRect(gutterWidth, 0, 1, height); - const cyclePos = isStatic ? totalTypingDuration + holdDuration : elapsed % cycleDuration; + const cyclePos = isStatic ? totalTypingDuration + 500 : elapsed % cycleDuration; const writingPhase = cyclePos < totalTypingDuration; let completedLines = codeLines.length; @@ -795,6 +795,11 @@ }; const render = (timestamp) => { + if (!pageVisible || !heroVisible || reducedMotionQuery.matches) { + rafId = null; + return; + } + const elapsed = timestamp - startAt; const palette = themes[getTheme()]; ctx.clearRect(0, 0, width, height); @@ -817,6 +822,10 @@ const start = () => { stop(); resizeCanvas(); + drawStatic(); + if (!pageVisible || !heroVisible || reducedMotionQuery.matches) { + return; + } startAt = performance.now(); rafId = requestAnimationFrame(render); }; @@ -848,5 +857,24 @@ window.addEventListener('resize', onResize); window.addEventListener('load', onResize); + const updateVisibility = () => { + pageVisible = document.visibilityState !== 'hidden'; + if (pageVisible && heroVisible) { + start(); + } else { + stop(); + } + }; + + document.addEventListener('visibilitychange', updateVisibility); + + if (typeof IntersectionObserver !== 'undefined') { + const visibilityObserver = new IntersectionObserver((entries) => { + heroVisible = entries.length === 0 || entries[0].isIntersecting; + updateVisibility(); + }, { threshold: 0.01 }); + visibilityObserver.observe(hero); + } + start(); })();