dml_frontend/pages/tools/ContrastChecker.vue

163 lines
3.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="contrastChecker">
<div class="input-section">
<label for="domain">Domain eingeben:</label>
<div class="row">
<input id="domain" v-model="domainInput" type="text" placeholder="z.B. example.com" />
<button :disabled="!isValidDomain" @click="loadDomain">Anzeigen</button>
</div>
<p v-if="domainInput && !isValidDomain" class="error">Ungültige Domain</p>
</div>
<iframe
v-if="iframeSrc"
:src="iframeSrc"
class="preview-frame"
sandbox="allow-same-origin allow-scripts"
></iframe>
<div class="color-buttons">
<button @click="pickColor('background')">Hintergrundfarbe wählen</button>
<button @click="pickColor('text')">Schriftfarbe wählen</button>
</div>
<div v-if="contrastRatio" class="result-box">
<p><strong>Kontrast:</strong> {{ contrastRatio.toFixed(2) }}</p>
<p><strong>WCAG Bewertung:</strong> {{ contrastLevel }}</p>
</div>
</div>
</template>
<script setup>
const domainInput = ref('')
const iframeSrc = ref('')
const bgColor = ref(null)
const textColor = ref(null)
const domainRegex = /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
const isValidDomain = computed(() => domainRegex.test(domainInput.value))
// Diese Funktionen hinzufügen
function hexToRgb(hex) {
if (!hex) return null
let c = hex.replace('#', '')
if (c.length === 3) {
c = c.split('').map((char) => char + char).join('')
}
const bigint = parseInt(c, 16)
return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255]
}
function luminance(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]
}
function getContrast(bgHex, fgHex) {
const bgRgb = hexToRgb(bgHex)
const fgRgb = hexToRgb(fgHex)
if (!bgRgb || !fgRgb) return null
const L1 = luminance(...bgRgb)
const L2 = luminance(...fgRgb)
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05)
}
function loadDomain() {
iframeSrc.value = 'https://' + domainInput.value
}
function pickColor(type) {
const input = document.createElement('input')
input.type = 'color'
input.style.display = 'none'
input.addEventListener('input', () => {
if (type === 'background') bgColor.value = input.value
if (type === 'text') textColor.value = input.value
})
document.body.appendChild(input)
input.click()
input.remove()
}
const contrastRatio = computed(() => {
if (!bgColor.value || !textColor.value) return null
try {
return getContrast(bgColor.value, textColor.value)
} catch (e) {
return null
}
})
const contrastLevel = computed(() => {
if (!contrastRatio.value) return 'unbekannt'
const ratio = contrastRatio.value
if (ratio >= 7) return 'AAA'
if (ratio >= 4.5) return 'AA'
if (ratio >= 3) return 'A'
return 'nicht ausreichend'
})
</script>
<style lang="sass">
.contrastChecker
max-width: 1200px
margin: 10vh auto 3rem auto
padding: 2rem
.input-section
margin-bottom: 1rem
.row
display: flex
gap: 0.5rem
margin-top: 0.5rem
input[type="text"]
flex: 1
padding: 0.5rem
border: 1px solid #ccc
border-radius: 4px
button
padding: 0.5rem 1rem
border: none
background-color: #007acc
color: white
cursor: pointer
border-radius: 4px
button:disabled
background-color: #ccc
cursor: not-allowed
.error
color: red
font-size: 0.9rem
margin-top: 0.25rem
.preview-frame
width: 100%
height: 300px
border: 1px solid #ccc
margin-top: 1rem
.color-buttons
margin-top: 1rem
display: flex
gap: 1rem
.result-box
margin-top: 1rem
padding: 1rem
border: 1px solid #ccc
border-radius: 4px
background: #f9f9f9
</style>