diff --git a/frontend/src/components/clients/ClientTrafficCell.tsx b/frontend/src/components/clients/ClientTrafficCell.tsx
index ed2a5f3fb..a99f643da 100644
--- a/frontend/src/components/clients/ClientTrafficCell.tsx
+++ b/frontend/src/components/clients/ClientTrafficCell.tsx
@@ -64,6 +64,7 @@ export default function ClientTrafficCell({
{SizeFormatter.sizeFormat(display.used)}
{display.isUnlimited ? (
-
+
) : (
diff --git a/frontend/src/components/clients/ConfigBlock.css b/frontend/src/components/clients/ConfigBlock.css
index c0b4193df..5a7567786 100644
--- a/frontend/src/components/clients/ConfigBlock.css
+++ b/frontend/src/components/clients/ConfigBlock.css
@@ -12,6 +12,10 @@
min-width: 0;
}
+body.light .config-block .ant-tag.ant-tag-filled.ant-tag-gold {
+ color: #874d00;
+}
+
.config-block .ant-collapse-extra {
display: flex;
align-items: center;
diff --git a/frontend/src/components/clients/ConfigBlock.tsx b/frontend/src/components/clients/ConfigBlock.tsx
index 71bc6f5b9..dc7b87ac3 100644
--- a/frontend/src/components/clients/ConfigBlock.tsx
+++ b/frontend/src/components/clients/ConfigBlock.tsx
@@ -68,6 +68,7 @@ export default function ConfigBlock({
{messageContextHolder}
{ textareaRef.current = (el as unknown as { resizableTextArea?: { textArea: HTMLTextAreaElement } })?.resizableTextArea?.textArea ?? null; }}
+ aria-label={title}
value={value}
onChange={(e) => setValue(e.target.value)}
autoSize={{ minRows: 10, maxRows: 20 }}
@@ -80,6 +81,7 @@ export default function PromptModal({
) : (
setValue(e.target.value)}
onKeyDown={onKeydown}
diff --git a/frontend/src/components/feedback/TextModal.tsx b/frontend/src/components/feedback/TextModal.tsx
index 978f445a7..fcb73b218 100644
--- a/frontend/src/components/feedback/TextModal.tsx
+++ b/frontend/src/components/feedback/TextModal.tsx
@@ -75,6 +75,7 @@ export default function TextModal({ open, onClose, title, content, fileName = ''
) : (
(function JsonEd
doc: value,
extensions: [
basicSetup,
+ EditorView.contentAttributes.of({ 'aria-label': t('jsonEditor') }),
keymap.of([indentWithTab]),
json(),
linter(jsonParseLinter()),
diff --git a/frontend/src/components/form/rhf/FormField.tsx b/frontend/src/components/form/rhf/FormField.tsx
index 0645d2418..89c1245e7 100644
--- a/frontend/src/components/form/rhf/FormField.tsx
+++ b/frontend/src/components/form/rhf/FormField.tsx
@@ -1,4 +1,4 @@
-import { cloneElement } from 'react';
+import { cloneElement, useId } from 'react';
import type { CSSProperties, ReactElement, ReactNode } from 'react';
import { Controller } from 'react-hook-form';
import type { Control, ControllerProps, FieldValues, Path } from 'react-hook-form';
@@ -48,6 +48,9 @@ export function FormField({
}: FormFieldProps) {
const { t } = useTranslation();
const dottedName = toDotted(name) as Path;
+ const generatedId = useId();
+ const explicitId = (children as ReactElement<{ id?: string }>).props.id;
+ const fieldId = explicitId ?? generatedId;
return (
({
? t(fieldState.error.message, { defaultValue: fieldState.error.message })
: undefined;
const childProps: Record = {
+ id: fieldId,
[valueProp]: displayValue,
onChange: (...args: unknown[]) => {
const raw = normalizeAntdOnChange(args, valueProp);
@@ -73,6 +77,7 @@ export function FormField({
return (
(
-
+ extra: ({ value, onChange, ariaLabel }) => (
+
),
},
{
key: 'memory.high',
label: 'eventMemoryHigh',
settingKey: 'smtpMemory',
- extra: ({ value, onChange }) => (
-
+ extra: ({ value, onChange, ariaLabel }) => (
+
),
},
],
diff --git a/frontend/src/components/ui/notifications/NotificationGroup.tsx b/frontend/src/components/ui/notifications/NotificationGroup.tsx
index a9c881bac..aec981105 100644
--- a/frontend/src/components/ui/notifications/NotificationGroup.tsx
+++ b/frontend/src/components/ui/notifications/NotificationGroup.tsx
@@ -51,6 +51,7 @@ export function NotificationGroup({ config, selected, onToggle, onToggleAll, all
{event.extra?.({
value: Number((allSetting as unknown as Record)[event.settingKey]) || 0,
onChange: (v) => updateSetting({ [event.settingKey]: v }),
+ ariaLabel: t(`pages.settings.${event.label}`),
})}
))}
diff --git a/frontend/src/components/ui/notifications/TelegramNotifications.tsx b/frontend/src/components/ui/notifications/TelegramNotifications.tsx
index 2ce63c4b1..21ba70c0b 100644
--- a/frontend/src/components/ui/notifications/TelegramNotifications.tsx
+++ b/frontend/src/components/ui/notifications/TelegramNotifications.tsx
@@ -37,16 +37,16 @@ const GROUPS: NotificationGroupConfig[] = [
key: 'cpu.high',
label: 'eventCPUHigh',
settingKey: 'tgCpu',
- extra: ({ value, onChange }) => (
-
+ extra: ({ value, onChange, ariaLabel }) => (
+
),
},
{
key: 'memory.high',
label: 'eventMemoryHigh',
settingKey: 'tgMemory',
- extra: ({ value, onChange }) => (
-
+ extra: ({ value, onChange, ariaLabel }) => (
+
),
},
],
diff --git a/frontend/src/components/ui/notifications/types.ts b/frontend/src/components/ui/notifications/types.ts
index baff64b95..208ee77a3 100644
--- a/frontend/src/components/ui/notifications/types.ts
+++ b/frontend/src/components/ui/notifications/types.ts
@@ -4,7 +4,7 @@ export interface NotificationEventConfig {
key: string;
label: string;
settingKey: string;
- extra?: (props: { value: number; onChange: (v: number | null) => void }) => ReactNode;
+ extra?: (props: { value: number; onChange: (v: number | null) => void; ariaLabel: string }) => ReactNode;
}
export interface NotificationGroupConfig {
diff --git a/frontend/src/hooks/useTheme.tsx b/frontend/src/hooks/useTheme.tsx
index 665e46f18..4ef11a081 100644
--- a/frontend/src/hooks/useTheme.tsx
+++ b/frontend/src/hooks/useTheme.tsx
@@ -78,13 +78,28 @@ const STATISTIC_TOKENS = {
contentFontSize: 17,
titleFontSize: 11,
};
+const LIGHT_CONTRAST_TOKENS = {
+ colorTextDescription: 'rgba(0, 0, 0, 0.58)',
+ colorTextTertiary: 'rgba(0, 0, 0, 0.58)',
+ colorTextPlaceholder: '#767676',
+ colorError: '#cf1322',
+ colorErrorText: '#cf1322',
+ colorSuccessText: '#237804',
+};
+const LIGHT_BUTTON_TOKENS = {
+ colorPrimary: '#0958d9',
+ colorPrimaryHover: '#2468e5',
+ colorPrimaryActive: '#073ea8',
+};
export function buildAntdThemeConfig(isDark: boolean, isUltra: boolean): ThemeConfig {
if (!isDark) {
return {
algorithm: antdTheme.defaultAlgorithm,
+ token: LIGHT_CONTRAST_TOKENS,
components: {
Statistic: STATISTIC_TOKENS,
+ Button: LIGHT_BUTTON_TOKENS,
},
};
}