Цветовая контрастность

Цветовая контрастность играет ключевую роль в обеспечении доступности веб-приложений. Она определяет, насколько различимы элементы интерфейса для пользователей с нарушениями зрения, включая дальтонизм. В Vue.js правильное управление цветовой контрастностью достигается через комбинацию реактивных данных, вычисляемых свойств и динамических стилей.


Реактивные данные и динамическая стилизация

Vue.js предоставляет возможность динамически изменять стили элементов с помощью директивы :style. Контрастность между цветом текста и фоном можно вычислять в зависимости от состояния приложения:

<script>
export default {
  data() {
    return {
      backgroundColor: '#ffffff',
      textColor: '#000000'
    };
  },
  computed: {
    contrastRatio() {
      const hexToRgb = hex => {
        const bigint = parseInt(hex.slice(1), 16);
        return {
          r: (bigint >> 16) & 255,
          g: (bigint >> 8) & 255,
          b: bigint & 255
        };
      };

      const lum = ({ r, g, b }) => {
        const a = [r, g, b].map(v => {
          v /= 255;
          return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
        });
        return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
      };

      const L1 = lum(hexToRgb(this.textColor));
      const L2 = lum(hexToRgb(this.backgroundColor));
      return L1 > L2 ? (L1 + 0.05) / (L2 + 0.05) : (L2 + 0.05) / (L1 + 0.05);
    },
    isAccessible() {
      return this.contrastRatio >= 4.5;
    }
  }
};
</script>

<template>
  <div :style="{ color: textColor, backgroundColor: backgroundColor }">
    Контрастность: {{ contrastRatio.toFixed(2) }}
    <span v-if="!isAccessible">(низкая доступность)</span>
  </div>
</template>

Ключевой момент: вычисляемое свойство contrastRatio позволяет динамически оценивать соответствие текста стандартам WCAG (уровень AA требует не менее 4.5:1 для обычного текста).


Динамическая смена тем и адаптивная контрастность

Для приложений с поддержкой светлой и тёмной темы Vue.js можно реализовать адаптивное изменение цветов с учётом контрастности:

data() {
  return {
    theme: 'light'
  };
},
computed: {
  backgroundColor() {
    return this.theme === 'light' ? '#ffffff' : '#1e1e1e';
  },
  textColor() {
    return this.theme === 'light' ? '#000000' : '#ffffff';
  }
}

Использование реактивной темы позволяет поддерживать читаемость интерфейса и корректную контрастность без ручного вмешательства для каждого компонента.


Компонентная структура и повторное использование

Контрастность удобнее контролировать через переиспользуемый компонент AccessibleText, который принимает цвет текста и фона как пропсы и автоматически вычисляет уровень доступности:

<template>
  <p :style="{ color, backgroundColor }">
    <slot></slot>
    <small v-if="!isAccessible">(контраст недостаточен)</small>
  </p>
</template>

<script>
export default {
  props: {
    color: { type: String, required: true },
    backgroundColor: { type: String, required: true }
  },
  computed: {
    contrastRatio() {
      const hexToRgb = hex => {
        const bigint = parseInt(hex.slice(1), 16);
        return {
          r: (bigint >> 16) & 255,
          g: (bigint >> 8) & 255,
          b: bigint & 255
        };
      };
      const lum = ({ r, g, b }) => {
        const a = [r, g, b].map(v => {
          v /= 255;
          return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
        });
        return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
      };
      const L1 = lum(hexToRgb(this.color));
      const L2 = lum(hexToRgb(this.backgroundColor));
      return L1 > L2 ? (L1 + 0.05) / (L2 + 0.05) : (L2 + 0.05) / (L1 + 0.05);
    },
    isAccessible() {
      return this.contrastRatio >= 4.5;
    }
  }
};
</script>

Такой подход позволяет централизованно управлять проверкой контрастности, улучшая масштабируемость и поддерживаемость проекта.


Интеграция с CSS-переменными

CSS-переменные дают возможность изменять цвета глобально без изменения логики компонентов:

:root {
  --background-light: #ffffff;
  --background-dark: #1e1e1e;
  --text-light: #000000;
  --text-dark: #ffffff;
}

В компонентах Vue можно ссылаться на эти переменные через style:

computed: {
  backgroundColor() {
    return this.theme === 'light' ? 'var(--background-light)' : 'var(--background-dark)';
  },
  textColor() {
    return this.theme === 'light' ? 'var(--text-light)' : 'var(--text-dark)';
  }
}

Использование CSS-переменных совместно с реактивными данными повышает гибкость управления цветовыми схемами и контрастностью.


Анимации и контрастность

При динамических изменениях темы или анимациях важно следить, чтобы контрастность оставалась достаточной на всех этапах перехода. В Vue.js для плавного изменения цвета используется директива :style с переходами:

.transition-color {
  transition: background-color 0.3s ease, color 0.3s ease;
}
<div :class="'transition-color'" :style="{ backgroundColor, color: textColor }">
  Динамический текст
</div>

Плавный переход не должен снижать читаемость текста, поэтому следует проверять контраст на всех промежуточных цветах.


Цветовая контрастность в Vue.js реализуется через комбинацию реактивных данных, вычисляемых свойств и CSS-переменных, что обеспечивает доступность и удобство поддержки интерфейсов. Центральная проверка контрастности через вычисляемые свойства и повторно используемые компоненты позволяет создать масштабируемую и безопасную для пользователей систему визуальных стилей.