style: 优化表单样式并提升表单性能

This commit is contained in:
2026-09-01 01:14:32 +08:00
parent 0901c9c99f
commit 8ceebab216
7 changed files with 737 additions and 349 deletions
+8 -43
View File
@@ -5,9 +5,7 @@
.itstudio-otp-wrap { .itstudio-otp-wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px;
width: 100%; width: 100%;
margin-top: 4px;
} }
.itstudio-otp-group { .itstudio-otp-group {
@@ -69,9 +67,9 @@
} }
.itstudio-otp-send.is-countdown { .itstudio-otp-send.is-countdown {
background: rgba(56, 139, 253, 0.12) !important; background: #6b7280 !important;
border: 1px solid rgba(56, 139, 253, 0.3) !important; border: 1px solid #7c8490 !important;
color: #58a6ff !important; color: #e5e7eb !important;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
font-weight: 600; font-weight: 600;
} }
@@ -82,28 +80,7 @@
} }
.itstudio-otp-status { .itstudio-otp-status {
display: flex; width: 100%;
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;
} }
/* 浅色模式适配 */ /* 浅色模式适配 */
@@ -118,21 +95,9 @@
} }
[data-theme="light"] .itstudio-otp-send.is-countdown { [data-theme="light"] .itstudio-otp-send.is-countdown {
background: rgba(9, 105, 218, 0.08) !important; background: #9ca3af !important;
border: 1px solid rgba(9, 105, 218, 0.25) !important; border: 1px solid #9ca3af !important;
color: #0969da !important; color: #ffffff !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;
} }
@media (max-width: 480px) { @media (max-width: 480px) {
@@ -142,4 +107,4 @@
.itstudio-otp-send { .itstudio-otp-send {
width: 100%; width: 100%;
} }
} }
+85 -14
View File
@@ -6,6 +6,8 @@
return; return;
} }
const OTP_COOLDOWN_SECONDS = 60;
function getLanguage() { function getLanguage() {
return document.documentElement.lang === 'en' ? 'en' : 'cn'; return document.documentElement.lang === 'en' ? 'en' : 'cn';
} }
@@ -41,12 +43,18 @@
} }
status.style.display = 'flex'; status.style.display = 'flex';
status.textContent = message; 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) { 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.disabled = true;
button.dataset.otpLocked = '1';
button.setAttribute('aria-disabled', 'true');
button.classList.add('is-countdown'); button.classList.add('is-countdown');
const update = function () { const update = function () {
@@ -55,6 +63,8 @@
if (remaining < 0) { if (remaining < 0) {
window.clearInterval(button.itstudioOtpTimer); window.clearInterval(button.itstudioOtpTimer);
button.itstudioOtpTimer = null; button.itstudioOtpTimer = null;
button.dataset.otpLocked = '0';
button.setAttribute('aria-disabled', 'false');
button.classList.remove('is-countdown'); button.classList.remove('is-countdown');
button.textContent = config.labels.send || '发送验证码'; button.textContent = config.labels.send || '发送验证码';
button.disabled = false; button.disabled = false;
@@ -65,7 +75,36 @@
button.itstudioOtpTimer = window.setInterval(update, 1000); 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) { async function sendCode(form, button, status) {
if (button.dataset.otpLocked === '1' || button.disabled) {
return;
}
const emailInput = findField(form, config.emailFieldId); const emailInput = findField(form, config.emailFieldId);
const email = emailInput ? emailInput.value.trim() : ''; const email = emailInput ? emailInput.value.trim() : '';
if (!email) { if (!email) {
@@ -79,7 +118,10 @@
return; return;
} }
// Block rapid duplicate clicks while the request is in flight.
button.disabled = true; button.disabled = true;
button.dataset.otpLocked = '1';
button.setAttribute('aria-disabled', 'true');
button.classList.add('is-loading'); button.classList.add('is-loading');
button.textContent = config.labels.sending || '发送中…'; button.textContent = config.labels.sending || '发送中…';
setStatus(status, config.labels.sending || '正在发送验证码…', 'info'); setStatus(status, config.labels.sending || '正在发送验证码…', 'info');
@@ -95,26 +137,26 @@
nonce: config.nonce nonce: config.nonce
}).toString() }).toString()
}); });
const result = await response.json(); const result = await readAjaxResult(response);
const payload = result && result.data ? result.data : {}; const payload = result.payload || {};
button.classList.remove('is-loading'); button.classList.remove('is-loading');
if (!result.success) { if (!result.success) {
setStatus(status, getMessage(payload, config.labels.networkError || '发送失败,请稍后重试。'), 'error'); setStatus(status, getMessage(payload, config.labels.networkError || '发送失败,请稍后重试。'), 'error');
if (payload.wait) { button.dataset.otpLocked = '0';
startCooldown(button, payload.wait); button.setAttribute('aria-disabled', 'false');
} else { button.textContent = config.labels.send || '发送验证码';
button.textContent = config.labels.send || '发送验证码'; button.disabled = false;
button.disabled = false;
}
return; return;
} }
setStatus(status, getMessage(payload, '验证码已发送,请检查邮箱及垃圾箱。'), 'success'); setStatus(status, getMessage(payload, '验证码已发送,请检查邮箱及垃圾箱。'), 'success');
startCooldown(button, payload.cooldown || config.cooldown); startCooldown(button, payload.cooldown || OTP_COOLDOWN_SECONDS);
} catch (error) { } catch (error) {
button.classList.remove('is-loading'); button.classList.remove('is-loading');
setStatus(status, config.labels.networkError || '网络错误,请稍后重试。', 'error'); setStatus(status, config.labels.networkError || '网络错误,请稍后重试。', 'error');
button.dataset.otpLocked = '0';
button.setAttribute('aria-disabled', 'false');
button.textContent = config.labels.send || '发送验证码'; button.textContent = config.labels.send || '发送验证码';
button.disabled = false; button.disabled = false;
} }
@@ -150,6 +192,8 @@
button.type = 'button'; button.type = 'button';
button.className = 'itstudio-otp-send'; button.className = 'itstudio-otp-send';
button.textContent = config.labels.send || '发送验证码'; button.textContent = config.labels.send || '发送验证码';
button.dataset.otpLocked = '0';
button.setAttribute('aria-disabled', 'false');
const status = document.createElement('div'); const status = document.createElement('div');
status.className = 'itstudio-otp-status'; 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(); mount();
const observer = new MutationObserver(mount); const observerRoot = document.querySelector('.join-form-content') || document.body;
observer.observe(document.body, {childList: true, subtree: true}); 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});
})();
@@ -57,6 +57,36 @@ function itstudio_join_otp_random_code() {
return (string) wp_rand(100000, 999999); 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()) { function itstudio_join_otp_json_error($message_cn, $message_en, $status = 400, $extra = array()) {
wp_send_json_error( wp_send_json_error(
array_merge( array_merge(
@@ -85,6 +115,10 @@ function itstudio_join_otp_send_ajax() {
itstudio_join_otp_json_error('请输入有效的邮箱地址。', 'Please enter a valid email address.', 400); 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(); $ip = itstudio_join_otp_get_client_ip();
$otp_key = itstudio_join_otp_key($email, $config['form_id']); $otp_key = itstudio_join_otp_key($email, $config['form_id']);
$existing = get_transient($otp_key); $existing = get_transient($otp_key);
+490 -277
View File
@@ -1,4 +1,4 @@
.join-page { .join-page {
padding: 36px 0 72px; padding: 36px 0 72px;
} }
@@ -659,32 +659,30 @@
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
/* =========================================
招新报名表单与 Formidable Forms 样式全面重构
========================================= */
.join-form-card { .join-form-card {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border: 1px solid color-mix(in srgb, var(--color-primary) 28%, var(--border-default) 72%); border: 1px solid color-mix(in srgb, var(--color-primary) 32%, var(--border-default) 68%);
border-radius: 6px; border-radius: 12px;
background: background:
linear-gradient(
118deg,
color-mix(in srgb, var(--color-primary) 12%, transparent) 0%,
transparent 36%
),
radial-gradient( radial-gradient(
120% 80% at 100% 0%, 120% 90% at 100% 0%,
color-mix(in srgb, var(--color-primary) 14%, transparent) 0%, color-mix(in srgb, var(--color-primary) 12%, transparent) 0%,
transparent 52% transparent 55%
), ),
linear-gradient( linear-gradient(
180deg, 180deg,
color-mix(in srgb, var(--bg-card) 84%, #0f1a2b 16%) 0%, color-mix(in srgb, var(--bg-card) 92%, #0d1117 8%) 0%,
color-mix(in srgb, var(--bg-card) 95%, transparent) 100% color-mix(in srgb, var(--bg-card) 98%, transparent) 100%
); );
padding: 18px; padding: 24px;
box-shadow: box-shadow:
0 22px 40px -32px color-mix(in srgb, #000000 78%, transparent), 0 16px 36px -20px color-mix(in srgb, #000000 70%, transparent),
inset 0 1px 0 color-mix(in srgb, #9bc9ff 18%, transparent), inset 0 1px 0 color-mix(in srgb, #9bc9ff 16%, transparent);
inset 0 -1px 0 color-mix(in srgb, #0a101a 70%, transparent);
} }
.join-form-card::before { .join-form-card::before {
@@ -695,32 +693,32 @@
background: background:
repeating-linear-gradient( repeating-linear-gradient(
0deg, 0deg,
color-mix(in srgb, var(--color-primary) 8%, transparent) 0 1px, color-mix(in srgb, var(--color-primary) 6%, transparent) 0 1px,
transparent 1px 13px transparent 1px 16px
), ),
repeating-linear-gradient( repeating-linear-gradient(
90deg, 90deg,
color-mix(in srgb, var(--color-primary) 7%, transparent) 0 1px, color-mix(in srgb, var(--color-primary) 5%, transparent) 0 1px,
transparent 1px 18px transparent 1px 20px
); );
opacity: 0.26; opacity: 0.22;
} }
.join-form-card::after { .join-form-card::after {
content: ""; content: "";
position: absolute; position: absolute;
inset: 10px; inset: 12px;
pointer-events: none; pointer-events: none;
background: background:
linear-gradient(90deg, color-mix(in srgb, var(--color-primary) 72%, transparent), transparent) linear-gradient(90deg, color-mix(in srgb, var(--color-primary) 70%, transparent), transparent)
top left / 88px 1px no-repeat, top left / 80px 1px no-repeat,
linear-gradient(180deg, color-mix(in srgb, var(--color-primary) 72%, transparent), transparent) linear-gradient(180deg, color-mix(in srgb, var(--color-primary) 70%, transparent), transparent)
top left / 1px 44px no-repeat, top left / 1px 40px no-repeat,
linear-gradient(270deg, color-mix(in srgb, var(--color-primary) 48%, transparent), transparent) linear-gradient(270deg, color-mix(in srgb, var(--color-primary) 50%, transparent), transparent)
bottom right / 66px 1px no-repeat, bottom right / 60px 1px no-repeat,
linear-gradient(0deg, color-mix(in srgb, var(--color-primary) 48%, transparent), transparent) linear-gradient(0deg, color-mix(in srgb, var(--color-primary) 50%, transparent), transparent)
bottom right / 1px 30px no-repeat; bottom right / 1px 30px no-repeat;
opacity: 0.65; opacity: 0.6;
} }
.join-form-card > * { .join-form-card > * {
@@ -729,7 +727,7 @@
} }
.join-form-card + .join-panel-title { .join-form-card + .join-panel-title {
margin-top: 16px; margin-top: 20px;
} }
.join-form-card-placeholder { .join-form-card-placeholder {
@@ -739,13 +737,14 @@
} }
.join-form-content { .join-form-content {
margin-top: 14px; margin-top: 10px;
} }
.join-form-content form { .join-form-content form {
margin: 0; margin: 0;
} }
/* --- 录取进度查询表单 --- */
.join-progress-query-form { .join-progress-query-form {
display: grid; display: grid;
gap: 12px; gap: 12px;
@@ -775,7 +774,7 @@
border-radius: 8px; border-radius: 8px;
background: color-mix(in srgb, var(--bg-card) 90%, transparent) !important; background: color-mix(in srgb, var(--bg-card) 90%, transparent) !important;
color: var(--text-primary) !important; color: var(--text-primary) !important;
padding: 9px 10px; padding: 10px 12px;
line-height: 1.3; line-height: 1.3;
transition: border-color var(--duration-fast), box-shadow var(--duration-fast), background-color var(--duration-fast); 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 { .join-progress-query-field input:focus {
outline: none; outline: none;
border-color: color-mix(in srgb, var(--color-primary) 55%, var(--border-default) 45%) !important; border-color: var(--color-primary) !important;
background: color-mix(in srgb, var(--bg-card) 90%, transparent) !important; background: color-mix(in srgb, var(--bg-card) 95%, transparent) !important;
color: var(--text-primary) !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, .join-progress-query-field input:-webkit-autofill,
@@ -827,23 +826,25 @@
.join-progress-query-submit { .join-progress-query-submit {
border: 1px solid color-mix(in srgb, var(--color-primary) 48%, var(--border-default) 52%); 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%); background: color-mix(in srgb, var(--color-primary) 18%, var(--bg-card) 82%);
color: var(--text-primary); color: var(--text-primary);
padding: 9px 16px; padding: 10px 18px;
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease;
} }
.join-progress-query-submit:hover { .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 { .join-progress-query-feedback {
margin: 2px 0 0; margin: 8px 0 0;
padding: 10px 12px; padding: 10px 14px;
border-radius: 10px; border-radius: 8px;
border: 1px solid color-mix(in srgb, var(--border-default) 80%, transparent); border: 1px solid color-mix(in srgb, var(--border-default) 80%, transparent);
background: color-mix(in srgb, var(--bg-card) 90%, transparent); background: color-mix(in srgb, var(--bg-card) 90%, transparent);
color: var(--text-primary); color: var(--text-primary);
@@ -871,296 +872,390 @@
background: color-mix(in srgb, #c34646 14%, var(--bg-card) 86%); 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; 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; max-width: none;
} }
.join-form-content .frm_style_formidable-style.with_frm_style, .join-form-content .frm_fields_container {
.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_fields, display: flex;
.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_field, flex-direction: column;
.join-form-content .frm_style_formidable-style.with_frm_style .frm_field_wrapper { gap: 16px;
color: color-mix(in srgb, var(--text-primary) 95%, #ffffff 5%) !important;
} }
.join-form-content .frm_style_formidable-style.with_frm_style .frm_form_field { .join-form-content .frm_form_field,
margin-bottom: 14px !important; .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 { .join-form-content .frm_primary_label,
color: color-mix(in srgb, var(--text-primary) 95%, #ffffff 5%) !important; .join-form-content label.frm_primary_label,
opacity: 1 !important; .join-form-content .frm_section_heading h3,
} .join-form-content .frm_inline_box label {
color: var(--text-primary) !important;
.join-form-content .frm_style_formidable-style.with_frm_style .frm_primary_label, font-size: 0.92rem !important;
.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;
font-weight: 600 !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; opacity: 1 !important;
letter-spacing: 0.01em;
} }
.join-form-content .frm_style_formidable-style.with_frm_style .frm_required { .join-form-content .frm_required {
color: #ff6767 !important; 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, /* --- 顶部全局错误/提示横幅 (Modern Alert Banner) --- */
.join-form-content .frm_style_formidable-style.with_frm_style .frm_error, .join-form-content .frm_error_style,
.join-form-content .frm_style_formidable-style.with_frm_style .frm_error_style { .join-form-content .frm_message.frm_error_style,
color: color-mix(in srgb, var(--text-primary) 82%, var(--text-secondary) 18%) !important; .join-form-content div.frm_error_style,
} .join-form-content .frm_error {
position: relative !important;
.join-form-content .frm_style_formidable-style.with_frm_style .frm_error_style, display: flex !important;
.join-form-content .frm_style_formidable-style.with_frm_style .frm_error { align-items: center !important;
border-left: 3px solid #ff6767 !important; gap: 10px !important;
background: color-mix(in srgb, #ff6767 10%, transparent) !important; padding: 12px 16px 12px 42px !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;
border-radius: 8px !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_error_style::before,
.join-form-content .frm_style_formidable-style.with_frm_style textarea::placeholder { .join-form-content .frm_message.frm_error_style::before,
color: color-mix(in srgb, var(--text-secondary) 78%, var(--text-primary) 22%) !important; .join-form-content .frm_error::before {
opacity: 1; 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, /* --- 成功提示 (Success Message) --- */
.join-form-content .frm_style_formidable-style.with_frm_style input[type="email"]:focus, .join-form-content .frm_message,
.join-form-content .frm_style_formidable-style.with_frm_style input[type="number"]:focus, .join-form-content .frm_success_style,
.join-form-content .frm_style_formidable-style.with_frm_style input[type="tel"]:focus, .join-form-content .frm_success {
.join-form-content .frm_style_formidable-style.with_frm_style input[type="date"]:focus, position: relative !important;
.join-form-content .frm_style_formidable-style.with_frm_style input[type="datetime-local"]:focus, display: flex !important;
.join-form-content .frm_style_formidable-style.with_frm_style textarea:focus, align-items: center !important;
.join-form-content .frm_style_formidable-style.with_frm_style select:focus { gap: 10px !important;
border-color: color-mix(in srgb, var(--color-primary) 76%, #8ec9ff 24%) !important; padding: 12px 16px 12px 42px !important;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 24%, transparent) !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; 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; 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-right: 12px !important;
padding-left: calc(var(--iti-selected-country-arrow-padding, 64px) + 4px) !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-top-left-radius: 8px;
border-bottom-left-radius: 8px; border-bottom-left-radius: 8px;
} }
.join-form-content .frm_style_formidable-style.with_frm_style input:-webkit-autofill, .join-form-content .iti__dropdown-content,
.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;
}
.frm_forms.with_frm_style .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; background: #161b22 !important;
border: 1px solid color-mix(in srgb, var(--border-default) 46%, var(--color-primary) 54%) !important; border: 1px solid rgba(56, 139, 253, 0.4) !important;
color: color-mix(in srgb, var(--text-primary) 92%, #ffffff 8%) !important; color: #f0f6fc !important;
box-shadow: 0 18px 36px -22px color-mix(in srgb, #000000 68%, transparent); box-shadow: 0 18px 36px -16px rgba(0, 0, 0, 0.75);
} border-radius: 8px;
.frm_forms.with_frm_style .iti__search-input-wrapper {
margin: 6px 8px !important;
} }
.join-form-content .iti__search-input,
.frm_forms.with_frm_style .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; border: 1px solid rgba(240, 246, 252, 0.2) !important;
background: color-mix(in srgb, var(--bg-body) 84%, #ffffff 16%) !important; background: #0d1117 !important;
color: color-mix(in srgb, var(--text-primary) 94%, #ffffff 6%) !important; color: #f0f6fc !important;
min-height: 40px !important; min-height: 38px !important;
} border-radius: 6px;
.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;
} }
.join-form-content .iti__country,
.frm_forms.with_frm_style .iti__country { .frm_forms.with_frm_style .iti__country {
color: color-mix(in srgb, var(--text-primary) 90%, #ffffff 10%) !important; color: #c9d1d9 !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;
} }
.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.iti__highlight,
.frm_forms.with_frm_style .iti__country:hover { .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 { .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"], /* --- 选项卡片化重构 (Checkbox & Radio Chips) --- */
.join-form-content .frm_style_formidable-style.with_frm_style input[type="radio"] { .join-form-content .frm_opt_container {
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 {
display: flex !important; display: flex !important;
flex-wrap: wrap; flex-wrap: wrap !important;
gap: 10px; gap: 8px 12px !important;
align-items: center; margin-top: 6px !important;
} }
.join-form-content .frm_style_formidable-style.with_frm_style input[type="submit"], .join-form-content .frm_checkbox,
.join-form-content .frm_style_formidable-style.with_frm_style button.frm_button_submit { .join-form-content .frm_radio {
border-radius: 10px; margin: 0 !important;
border: 1px solid color-mix(in srgb, var(--color-primary) 82%, #9cd3ff 18%) !important; display: inline-flex !important;
background: linear-gradient( align-items: center !important;
180deg, }
color-mix(in srgb, var(--color-primary) 82%, #78c3ff 18%) 0%,
color-mix(in srgb, var(--color-primary) 92%, #5ea7f8 8%) 100% .join-form-content .frm_checkbox label,
) !important; .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; color: #ffffff !important;
font-weight: 700 !important; font-size: 0.95rem !important;
letter-spacing: 0.01em; font-weight: 600 !important;
min-height: 44px; letter-spacing: 0.02em !important;
padding: 0 20px !important; cursor: pointer !important;
box-shadow: 0 8px 18px -12px color-mix(in srgb, var(--color-primary) 65%, transparent); 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 input[type="submit"]:hover,
.join-form-content .frm_style_formidable-style.with_frm_style button.frm_button_submit:hover { .join-form-content button.frm_button_submit:hover {
filter: brightness(1.05); 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 input[type="submit"]:active,
.join-form-content .frm_style_formidable-style.with_frm_style button[name*="draft"], .join-form-content button.frm_button_submit:active {
.join-form-content .frm_style_formidable-style.with_frm_style input[name*="draft"], transform: translateY(0) !important;
.join-form-content .frm_style_formidable-style.with_frm_style button[value*="草稿"], filter: brightness(0.96) !important;
.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"] { .join-form-content .frm_save_draft,
border: 1px solid color-mix(in srgb, var(--border-default) 30%, var(--color-primary) 70%) !important; .join-form-content button[name*="draft"],
background: color-mix(in srgb, var(--bg-card) 88%, transparent) !important; .join-form-content input[name*="draft"],
color: var(--color-primary) !important; .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; 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_save_draft:hover,
.join-form-content .frm_style_formidable-style.with_frm_style button[name*="draft"]:hover, .join-form-content button[name*="draft"]:hover,
.join-form-content .frm_style_formidable-style.with_frm_style input[name*="draft"]:hover { .join-form-content input[name*="draft"]:hover {
background: color-mix(in srgb, var(--bg-card) 74%, var(--color-primary) 26%) !important; border-color: var(--color-primary) !important;
color: #ffffff !important; background: rgba(56, 139, 253, 0.1) !important;
color: var(--color-primary) !important;
} }
/* =========================================
浅色模式适配 (Light Theme)
========================================= */
[data-theme="light"] .join-form-card { [data-theme="light"] .join-form-card {
border-color: color-mix(in srgb, #8bbcff 52%, var(--border-default) 48%); border-color: color-mix(in srgb, #8bbcff 52%, var(--border-default) 48%);
background: background:
linear-gradient(
118deg,
color-mix(in srgb, #d9eaff 42%, transparent) 0%,
transparent 40%
),
radial-gradient( radial-gradient(
140% 90% at 100% 0%, 140% 90% at 100% 0%,
color-mix(in srgb, #dbe9ff 48%, transparent) 0%, color-mix(in srgb, #dbe9ff 48%, transparent) 0%,
@@ -1176,16 +1271,134 @@
inset 0 1px 0 rgba(255, 255, 255, 0.7); 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 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 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 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 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 input[type="password"],
[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style input[type="datetime-local"], [data-theme="light"] .join-form-content input[type="date"],
[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style textarea, [data-theme="light"] .join-form-content input[type="datetime-local"],
[data-theme="light"] .join-form-content .frm_style_formidable-style.with_frm_style select { [data-theme="light"] .join-form-content textarea,
[data-theme="light"] .join-form-content select {
background: #ffffff !important; 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 { .join-form-tip {
@@ -13,6 +13,8 @@
let height = 0; let height = 0;
let dpr = 1; let dpr = 1;
let rafId = null; let rafId = null;
let pageVisible = document.visibilityState !== 'hidden';
let heroVisible = true;
const waveDefs = [ const waveDefs = [
{ yRatio: 0.3, amplitude: 24, wavelength: 940, speed: 0.5, alpha: 0.98 }, { yRatio: 0.3, amplitude: 24, wavelength: 940, speed: 0.5, alpha: 0.98 },
@@ -45,7 +47,7 @@
}; };
const resize = () => { const resize = () => {
dpr = window.devicePixelRatio || 1; dpr = Math.min(window.devicePixelRatio || 1, 2);
width = main.clientWidth; width = main.clientWidth;
height = Math.max(1, Math.round(getCoverageHeight())); height = Math.max(1, Math.round(getCoverageHeight()));
canvas.width = Math.floor(width * dpr); canvas.width = Math.floor(width * dpr);
@@ -86,6 +88,11 @@
}; };
const render = (timestamp) => { const render = (timestamp) => {
if (!pageVisible || !heroVisible || prefersReduced.matches) {
rafId = null;
return;
}
const palette = palettes[getTheme()]; const palette = palettes[getTheme()];
const time = timestamp * 0.001; const time = timestamp * 0.001;
ctx.clearRect(0, 0, width, height); ctx.clearRect(0, 0, width, height);
@@ -111,7 +118,7 @@
const start = () => { const start = () => {
stop(); stop();
resize(); resize();
if (prefersReduced.matches) { if (prefersReduced.matches || !pageVisible || !heroVisible) {
drawStatic(); drawStatic();
return; return;
} }
@@ -136,5 +143,24 @@
window.addEventListener('resize', onResize); window.addEventListener('resize', onResize);
window.addEventListener('load', 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(); start();
})(); })();
@@ -34,6 +34,8 @@
let trackWidth = 0; let trackWidth = 0;
let stageMarkers = []; let stageMarkers = [];
let stageLayout = null; 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 waveShape1 = { base: 0.16, amp: 7.5, len: 280, speed: 0.9 };
const waveShape2 = { base: 0.44, amp: 6, len: 340, speed: 0.75 }; const waveShape2 = { base: 0.44, amp: 6, len: 340, speed: 0.75 };
@@ -166,7 +168,7 @@
} }
function resize() { function resize() {
dpr = window.devicePixelRatio || 1; dpr = Math.min(window.devicePixelRatio || 1, 2);
width = canvas.clientWidth; width = canvas.clientWidth;
height = canvas.clientHeight; height = canvas.clientHeight;
canvas.width = Math.max(1, Math.floor(width * dpr)); canvas.width = Math.max(1, Math.floor(width * dpr));
@@ -245,6 +247,11 @@
} }
function renderWaves(timestamp) { function renderWaves(timestamp) {
if (!pageVisible || !canvasVisible || prefersReducedMotion.matches) {
rafId = 0;
return;
}
const theme = getTheme(); const theme = getTheme();
const palette = palettes[theme]; const palette = palettes[theme];
const time = timestamp * 0.001; const time = timestamp * 0.001;
@@ -279,7 +286,7 @@
function startWaves() { function startWaves() {
stopWaves(); stopWaves();
resize(); resize();
if (prefersReducedMotion.matches) { if (prefersReducedMotion.matches || !pageVisible || !canvasVisible) {
drawStaticWaves(); drawStaticWaves();
return; return;
} }
@@ -604,6 +611,25 @@
}); });
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); 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') { if (typeof prefersReducedMotion.addEventListener === 'function') {
prefersReducedMotion.addEventListener('change', handleMotionPreferenceChange); prefersReducedMotion.addEventListener('change', handleMotionPreferenceChange);
} else if (typeof prefersReducedMotion.addListener === 'function') { } else if (typeof prefersReducedMotion.addListener === 'function') {
@@ -830,8 +856,9 @@
return; return;
} }
const successSelector = '.frm_message:not(.itstudio-otp-status), .frm_success_style, .frm_success';
const updateSuccessMessage = () => { const updateSuccessMessage = () => {
const successNode = formHost.querySelector('.frm_message, .frm_success_style, .frm_success'); const successNode = formHost.querySelector(successSelector);
if (!successNode) { if (!successNode) {
return; return;
} }
@@ -843,13 +870,37 @@
const messageCn = resolvedIntent === 'draft' ? '草稿已保存' : '你的报名表单已提交,感谢报名'; const messageCn = resolvedIntent === 'draft' ? '草稿已保存' : '你的报名表单已提交,感谢报名';
const messageEn = resolvedIntent === 'draft' ? 'Draft saved.' : 'Your registration form has been submitted. Thank you for applying.'; 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.cn = messageCn;
successNode.dataset.en = messageEn; successNode.dataset.en = messageEn;
successNode.textContent = pickLocaleText(messageCn, messageEn); successNode.textContent = nextText;
showJoinSubmitNotice(resolvedIntent); 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 }); observer.observe(formHost, { childList: true, subtree: true });
} }
@@ -13,6 +13,8 @@
let dpr = 1; let dpr = 1;
let rafId = null; let rafId = null;
let startAt = performance.now(); let startAt = performance.now();
let pageVisible = document.visibilityState !== 'hidden';
let heroVisible = true;
const EMPTY_SET = new Set(); const EMPTY_SET = new Set();
const WORD_RE = /^[A-Za-z_][A-Za-z0-9_]*$/; 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 clamp = (value, min, max) => Math.max(min, Math.min(max, value));
const getTheme = () => (document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'); const getTheme = () => (document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light');
const tokenCount = (tokens) => tokens.reduce((sum, token) => sum + token[0].length, 0); 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 resizeCanvas = () => {
const rect = hero.getBoundingClientRect(); const rect = hero.getBoundingClientRect();
width = Math.max(1, Math.round(rect.width)); width = Math.max(1, Math.round(rect.width));
height = Math.max(1, Math.round(rect.height)); 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.width = Math.floor(width * dpr);
canvas.height = Math.floor(height * dpr); canvas.height = Math.floor(height * dpr);
canvas.style.width = `${width}px`; canvas.style.width = `${width}px`;
@@ -668,12 +674,6 @@
const baseLineNo = 1; const baseLineNo = 1;
const scrollLeadLines = 2; 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.fillStyle = palette.background;
ctx.fillRect(0, 0, width, height); ctx.fillRect(0, 0, width, height);
ctx.fillStyle = palette.editor; ctx.fillStyle = palette.editor;
@@ -683,7 +683,7 @@
ctx.fillStyle = palette.divider; ctx.fillStyle = palette.divider;
ctx.fillRect(gutterWidth, 0, 1, height); ctx.fillRect(gutterWidth, 0, 1, height);
const cyclePos = isStatic ? totalTypingDuration + holdDuration : elapsed % cycleDuration; const cyclePos = isStatic ? totalTypingDuration + 500 : elapsed % cycleDuration;
const writingPhase = cyclePos < totalTypingDuration; const writingPhase = cyclePos < totalTypingDuration;
let completedLines = codeLines.length; let completedLines = codeLines.length;
@@ -795,6 +795,11 @@
}; };
const render = (timestamp) => { const render = (timestamp) => {
if (!pageVisible || !heroVisible || reducedMotionQuery.matches) {
rafId = null;
return;
}
const elapsed = timestamp - startAt; const elapsed = timestamp - startAt;
const palette = themes[getTheme()]; const palette = themes[getTheme()];
ctx.clearRect(0, 0, width, height); ctx.clearRect(0, 0, width, height);
@@ -817,6 +822,10 @@
const start = () => { const start = () => {
stop(); stop();
resizeCanvas(); resizeCanvas();
drawStatic();
if (!pageVisible || !heroVisible || reducedMotionQuery.matches) {
return;
}
startAt = performance.now(); startAt = performance.now();
rafId = requestAnimationFrame(render); rafId = requestAnimationFrame(render);
}; };
@@ -848,5 +857,24 @@
window.addEventListener('resize', onResize); window.addEventListener('resize', onResize);
window.addEventListener('load', 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(); start();
})(); })();