From cb0d9745c86e9f8b893c7e6be6fd1c6227acae1a Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:19:53 +0000 Subject: [PATCH 01/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ’ก What: - ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋น„์›Œ์งˆ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๊ณ  ์นจ๋ฌตํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ, "This field is required."๋ผ๋Š” ๋ช…์‹œ์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. - `aria-invalid="true"` ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ๋“ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์— ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ธ๋ผ์ธ ์Šคํƒ€์ผ(`preview.style.color = '#dc3545'`)์„ ์ง€์ •ํ•˜๋˜ ๋ฐฉ์‹์„ ์ œ๊ฑฐํ•˜๊ณ , ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด `preview.style.color = ''`๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๋’ค ๊ธฐ์กด ํด๋ž˜์Šค์ธ `.required-star`๋ฅผ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๐ŸŽฏ Why: - ํ•„์ˆ˜ ๊ฐ’์ด ๋น„์›Œ์ง„ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ์ž ๋ฐ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ๊ณผ ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. - ์ธ๋ผ์ธ ์Šคํƒ€์ผ์€ CSS ํด๋ž˜์Šค๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„ ์Šคํƒ€์ผ ๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ธฐ์กด ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•จ์œผ๋กœ์จ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๐Ÿ“ธ Before/After: - Before: ํŒŒ์ผ ์ž…๋ ฅ์ด๋‚˜ ๋ฐ”์ดํŠธ ๋ชฉํ‘œ์น˜ ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์งˆ ๋•Œ ์‹œ๊ฐ์ , ๊ตฌ์กฐ์ ์œผ๋กœ ์•„๋ฌด๋Ÿฐ ํ”ผ๋“œ๋ฐฑ์ด ์ œ๊ณต๋˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ์˜ค๋ฅ˜ ํ…์ŠคํŠธ๊ฐ€ ์‚ฌ๋ผ์กŒ์œผ๋ฉฐ, ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ปค์Šคํ…€ ์ธ๋ผ์ธ ์ƒ‰์ƒ์ด ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - After: ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์ง€๋ฉด ๋นจ๊ฐ„์ƒ‰ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉฐ, ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์šฉ `aria-invalid="true"` ์ƒํƒœ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ปค์Šคํ…€ ์ธ๋ผ์ธ CSS ๋Œ€์‹  `.required-star` ํด๋ž˜์Šค๊ฐ€ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค. โ™ฟ Accessibility: - ๋นˆ ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ์ƒํƒœ์—์„œ `aria-invalid="true"`๋ฅผ ๋™์ ์œผ๋กœ ๋ถ€์—ฌํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ํผ ์—๋Ÿฌ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. --- saas_web.py | 32 ++++++++++++++++++++------- tests/test_empty_target_validation.py | 6 ++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/saas_web.py b/saas_web.py index 63265e94..d077b361 100644 --- a/saas_web.py +++ b/saas_web.py @@ -223,9 +223,14 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('file_size_preview'); input.setCustomValidity(''); input.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#0f6674'; if (!file) { - preview.innerText = ''; + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + input.setCustomValidity('This field is required.'); + input.setAttribute('aria-invalid', 'true'); return; } const text = formatBinaryBytes(file.size); @@ -245,6 +250,7 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('target_bytes_preview'); this.setCustomValidity(''); this.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#1e7e34'; const buttons = document.querySelectorAll('#preset_buttons_container .preset-btn'); @@ -257,9 +263,11 @@ async def add_security_headers(request: Request, call_next): }); if (this.value === '') { - preview.innerText = ''; - this.setCustomValidity(''); - this.removeAttribute('aria-invalid'); + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + this.setCustomValidity('This field is required.'); + this.setAttribute('aria-invalid', 'true'); return; } @@ -279,6 +287,7 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('batch_target_bytes_preview'); this.setCustomValidity(''); this.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#1e7e34'; const buttons = document.querySelectorAll('#batch_preset_buttons_container .preset-btn'); @@ -291,9 +300,11 @@ async def add_security_headers(request: Request, call_next): }); if (this.value === '') { - preview.innerText = ''; - this.setCustomValidity(''); - this.removeAttribute('aria-invalid'); + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + this.setCustomValidity('This field is required.'); + this.setAttribute('aria-invalid', 'true'); return; } @@ -320,11 +331,16 @@ async def add_security_headers(request: Request, call_next): const preview = document.getElementById('batch_files_preview'); input.setCustomValidity(''); input.removeAttribute('aria-invalid'); + preview.classList.remove('required-star'); preview.style.color = '#0f6674'; const files = input.files; if (!files || files.length === 0) { - preview.innerText = ''; + preview.innerText = 'This field is required.'; + preview.style.color = ''; + preview.classList.add('required-star'); + input.setCustomValidity('This field is required.'); + input.setAttribute('aria-invalid', 'true'); return; } diff --git a/tests/test_empty_target_validation.py b/tests/test_empty_target_validation.py index 56374432..37afb0cf 100644 --- a/tests/test_empty_target_validation.py +++ b/tests/test_empty_target_validation.py @@ -51,9 +51,9 @@ def _assert_empty_branch(self, handler: str) -> None: empty_marker = "if (this.value === '') {" invalid_marker = "if (isNaN(val) || val <= 0) {" self.assertIn(empty_marker, handler) - self.assertIn("preview.innerText = '';", handler) - self.assertIn("this.setCustomValidity('');", handler) - self.assertIn("this.removeAttribute('aria-invalid');", handler) + self.assertIn("preview.innerText = 'This field is required.';", handler) + self.assertIn("this.setCustomValidity('This field is required.');", handler) + self.assertIn("this.setAttribute('aria-invalid', 'true');", handler) self.assertIn( "return;", handler[handler.index(empty_marker) : handler.index(invalid_marker)], From cfe17a1ab1d63d65b111ed6040cd31f5716afed7 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:06:34 +0000 Subject: [PATCH 02/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ’ก What: - ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋น„์›Œ์งˆ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๊ณ  ์นจ๋ฌตํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ, "This field is required."๋ผ๋Š” ๋ช…์‹œ์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. - `aria-invalid="true"` ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ๋“ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์— ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ธ๋ผ์ธ ์Šคํƒ€์ผ(`preview.style.color = '#dc3545'`)์„ ์ง€์ •ํ•˜๋˜ ๋ฐฉ์‹์„ ์ œ๊ฑฐํ•˜๊ณ , ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด `preview.style.color = ''`๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๋’ค ๊ธฐ์กด ํด๋ž˜์Šค์ธ `.required-star`๋ฅผ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๐ŸŽฏ Why: - ํ•„์ˆ˜ ๊ฐ’์ด ๋น„์›Œ์ง„ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ์ž ๋ฐ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ๊ณผ ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. - ์ธ๋ผ์ธ ์Šคํƒ€์ผ์€ CSS ํด๋ž˜์Šค๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„ ์Šคํƒ€์ผ ๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ธฐ์กด ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•จ์œผ๋กœ์จ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๐Ÿ“ธ Before/After: - Before: ํŒŒ์ผ ์ž…๋ ฅ์ด๋‚˜ ๋ฐ”์ดํŠธ ๋ชฉํ‘œ์น˜ ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์งˆ ๋•Œ ์‹œ๊ฐ์ , ๊ตฌ์กฐ์ ์œผ๋กœ ์•„๋ฌด๋Ÿฐ ํ”ผ๋“œ๋ฐฑ์ด ์ œ๊ณต๋˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ์˜ค๋ฅ˜ ํ…์ŠคํŠธ๊ฐ€ ์‚ฌ๋ผ์กŒ์œผ๋ฉฐ, ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ปค์Šคํ…€ ์ธ๋ผ์ธ ์ƒ‰์ƒ์ด ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - After: ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์ง€๋ฉด ๋นจ๊ฐ„์ƒ‰ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉฐ, ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์šฉ `aria-invalid="true"` ์ƒํƒœ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ปค์Šคํ…€ ์ธ๋ผ์ธ CSS ๋Œ€์‹  `.required-star` ํด๋ž˜์Šค๊ฐ€ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค. โ™ฟ Accessibility: - ๋นˆ ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ์ƒํƒœ์—์„œ `aria-invalid="true"`๋ฅผ ๋™์ ์œผ๋กœ ๋ถ€์—ฌํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ํผ ์—๋Ÿฌ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. From ea60b2b73cec048495cb14647007d6ccfbe0c389 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:48:27 +0000 Subject: [PATCH 03/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ’ก What: - ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋น„์›Œ์งˆ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๊ณ  ์นจ๋ฌตํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ, "This field is required."๋ผ๋Š” ๋ช…์‹œ์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. - `aria-invalid="true"` ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ๋“ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์— ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ธ๋ผ์ธ ์Šคํƒ€์ผ(`preview.style.color = '#dc3545'`)์„ ์ง€์ •ํ•˜๋˜ ๋ฐฉ์‹์„ ์ œ๊ฑฐํ•˜๊ณ , ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด `preview.style.color = ''`๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๋’ค ๊ธฐ์กด ํด๋ž˜์Šค์ธ `.required-star`๋ฅผ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๐ŸŽฏ Why: - ํ•„์ˆ˜ ๊ฐ’์ด ๋น„์›Œ์ง„ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ์ž ๋ฐ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ๊ณผ ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. - ์ธ๋ผ์ธ ์Šคํƒ€์ผ์€ CSS ํด๋ž˜์Šค๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„ ์Šคํƒ€์ผ ๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ธฐ์กด ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•จ์œผ๋กœ์จ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๐Ÿ“ธ Before/After: - Before: ํŒŒ์ผ ์ž…๋ ฅ์ด๋‚˜ ๋ฐ”์ดํŠธ ๋ชฉํ‘œ์น˜ ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์งˆ ๋•Œ ์‹œ๊ฐ์ , ๊ตฌ์กฐ์ ์œผ๋กœ ์•„๋ฌด๋Ÿฐ ํ”ผ๋“œ๋ฐฑ์ด ์ œ๊ณต๋˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ์˜ค๋ฅ˜ ํ…์ŠคํŠธ๊ฐ€ ์‚ฌ๋ผ์กŒ์œผ๋ฉฐ, ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ปค์Šคํ…€ ์ธ๋ผ์ธ ์ƒ‰์ƒ์ด ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. - After: ์ž…๋ ฅ๋ž€์ด ์ง€์›Œ์ง€๋ฉด ๋นจ๊ฐ„์ƒ‰ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉฐ, ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์šฉ `aria-invalid="true"` ์ƒํƒœ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ปค์Šคํ…€ ์ธ๋ผ์ธ CSS ๋Œ€์‹  `.required-star` ํด๋ž˜์Šค๊ฐ€ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค. โ™ฟ Accessibility: - ๋นˆ ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ์ƒํƒœ์—์„œ `aria-invalid="true"`๋ฅผ ๋™์ ์œผ๋กœ ๋ถ€์—ฌํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ํผ ์—๋Ÿฌ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ ‘๊ทผ์„ฑ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. From 55da6a757a0714b4103ebacab0e4e3bf876f4829 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:14:31 +0000 Subject: [PATCH 04/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ’ก What: - ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋น„์›Œ์งˆ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๊ณ  ์นจ๋ฌตํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ, "This field is required."๋ผ๋Š” ๋ช…์‹œ์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. - `aria-invalid="true"` ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ๋“ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์— ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ธ๋ผ์ธ ์Šคํƒ€์ผ(`preview.style.color = '#dc3545'`)์„ ์ง€์ •ํ•˜๋˜ ๋ฐฉ์‹์„ ์ œ๊ฑฐํ•˜๊ณ , ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด `preview.style.color = ''`๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๋’ค ๊ธฐ์กด ํด๋ž˜์Šค์ธ `.required-star`๋ฅผ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฐ˜์˜: ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์‹œ์ ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ` - -
-

Shrink Multiple Files

-
-

-
- -
Select several audio or video files, or drag and drop them here. You get back one zip with every output plus a results.json manifest. -
-

-

-
- -
Maximum allowed size in bytes for each output file -
1.86 GiB -

- - - - -
-

- -
-
""" From f6c821164599d20ac45dcb2b6ce627059e180a34 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:36:35 +0000 Subject: [PATCH 05/21] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ’ก What: - ํ•„์ˆ˜ ํผ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋น„์›Œ์งˆ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๊ณ  ์นจ๋ฌตํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ, "This field is required."๋ผ๋Š” ๋ช…์‹œ์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. - `aria-invalid="true"` ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ๋“ฑ ๋ณด์กฐ ๊ธฐ๊ธฐ์— ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ธ๋ผ์ธ ์Šคํƒ€์ผ(`preview.style.color = '#dc3545'`)์„ ์ง€์ •ํ•˜๋˜ ๋ฐฉ์‹์„ ์ œ๊ฑฐํ•˜๊ณ , ์ธ๋ผ์ธ ์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด `preview.style.color = ''`๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๋’ค ๊ธฐ์กด ํด๋ž˜์Šค์ธ `.required-star`๋ฅผ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฐ˜์˜: ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์‹œ์ ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด `