first commit
This commit is contained in:
3
src/icons/Check.svg
Normal file
3
src/icons/Check.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3334 4L6.00002 11.3333L2.66669 8" stroke="#F5F5F5" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
3
src/icons/Search.svg
Normal file
3
src/icons/Search.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 14L11.1 11.1M12.6667 7.33333C12.6667 10.2789 10.2789 12.6667 7.33333 12.6667C4.38781 12.6667 2 10.2789 2 7.33333C2 4.38781 4.38781 2 7.33333 2C10.2789 2 12.6667 4.38781 12.6667 7.33333Z" stroke="#1E1E1E" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 388 B |
70
src/index.html
Normal file
70
src/index.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>To Do List</title>
|
||||
<link rel="stylesheet" href="styles/index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="todo data-js-todo">
|
||||
<h1 class="todo_title">To Do List</h1>
|
||||
<form class="todo_form" data-js-todo-new-task-form>
|
||||
<div class="todo_field field">
|
||||
<label class="field_label" for="new_task">
|
||||
New task title
|
||||
</label>
|
||||
<input class="field_input" id="new_task" placeholder=" " autocomplete="off" data-js-todo-new-task-input>
|
||||
</div>
|
||||
<button class="button" type="submit">Add</button>
|
||||
</form>
|
||||
<form class="todo_form" data-js-todo-search-task-form>
|
||||
<div class="todo_field field">
|
||||
<label class="field_label" for="search_task">
|
||||
Search task
|
||||
</label>
|
||||
<input class="field_input" id="search_task" placeholder=" " autocomplete="off" type="search" data-js-todo-search-task-input>
|
||||
</div>
|
||||
</form>
|
||||
<div class="todo_info">
|
||||
<div class="todo_total-tasks">
|
||||
Total Tasks: <span data-js-todo-total-tasks>0</span>
|
||||
</div>
|
||||
<button class="todo_delete-all-button" type="button" data-js-todo-delete-all-button>
|
||||
Delete All
|
||||
</button>
|
||||
</div>
|
||||
<ul class="todo_list" data-js-todo-list>
|
||||
<li class="todo_item todo-item" data-js-todo-item>
|
||||
<input class="todo_item_checkbox" type="checkbox" id="todo-1" data-js-todo-item-checkbox>
|
||||
<label class="todo-item_label" for="todo-1" data-js-todo-item-label>Todo 1</label>
|
||||
<button class="todo_item_delete-button" type="button" aria-label="Delete" title="Delete" data-js-todo-item-delete-button>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 5L5 15M5 5L15 15" stroke="#757575" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
</li>
|
||||
<li class="todo_item todo-item" data-js-todo-item>
|
||||
<input class="todo_item_checkbox" type="checkbox" id="todo-1" data-js-todo-item-checkbox>
|
||||
<label class="todo-item_label" for="todo-1" data-js-todo-item-label>Todo 1</label>
|
||||
<button class="todo_item_delete-button" type="button" aria-label="Delete" title="Delete" data-js-todo-item-delete-button>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 5L5 15M5 5L15 15" stroke="#757575" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="todo_empty-message" data-js-todo-empty-message>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="main.js" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
0
src/main.js
Normal file
0
src/main.js
Normal file
19
src/styles/components/button.css
Normal file
19
src/styles/components/button.css
Normal file
@@ -0,0 +1,19 @@
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--input-height);
|
||||
padding: 12px;
|
||||
color: var(--color-gray-1);
|
||||
background-color: var(--color-dark-2);
|
||||
border: 1px solid var(--color-dark-2);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-dark-2);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
scale: 1.05;
|
||||
}
|
||||
}
|
||||
48
src/styles/components/field.css
Normal file
48
src/styles/components/field.css
Normal file
@@ -0,0 +1,48 @@
|
||||
.field {
|
||||
position: relative;
|
||||
|
||||
&:has(.field_input:not(:placeholder-shown)) .field_label {
|
||||
scale: 0.7;
|
||||
translate: -1.75em -2.6em;
|
||||
color: var(--color-black);
|
||||
}
|
||||
}
|
||||
.field_label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 17px;
|
||||
color: var(--color-gray-3);
|
||||
translate: 0 -50%;
|
||||
}
|
||||
.field_input {
|
||||
--fieldInputPaddingX: 16px;
|
||||
--fieldSearchInputIconSize: 16px;
|
||||
|
||||
width: 100%;
|
||||
height: var(--input-height);
|
||||
padding-inline: var(--fieldInputPaddingX);
|
||||
background-color: transparent;
|
||||
border: var(--border);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:hover, &:focus {
|
||||
color: var(--color-dark-2);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: var(--colorgray-1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&[type="search"] {
|
||||
&:placeholder-shown{
|
||||
padding-right: calc(
|
||||
var(--fieldInputPaddingX)*2+var(--fieldSearchInputIconSize)
|
||||
);
|
||||
background-color: url('/src/icons/Search.svg');
|
||||
background-position: calc(100% - var(--fieldInputPaddingX)) 50%;
|
||||
background-size: var(--fieldSearchInputIconSize);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
src/styles/components/todo-item.css
Normal file
99
src/styles/components/todo-item.css
Normal file
@@ -0,0 +1,99 @@
|
||||
.todo_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 12px;
|
||||
padding-left: 10px;
|
||||
border: var(--border);
|
||||
border-radius: var(--border-radius);
|
||||
transition-duration: var(--transition-duration);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-gray-1);
|
||||
|
||||
}
|
||||
|
||||
&.is-disappearing {
|
||||
opacity: 0;
|
||||
translate: 0 -75%;
|
||||
transition-duration: calc(var(--transition-duration)*2);
|
||||
pointer-events: none;
|
||||
|
||||
& ~ .todo_item {
|
||||
translate: 0 calc((100% + var(--todoListRowGap)) * -1);
|
||||
transition-delay: var(--transition-duration);
|
||||
transition-duration: var(--transition-duration);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.todo_item_checkbox {
|
||||
flex-shrink: 0;
|
||||
appearance: none;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-gray-4);
|
||||
border-radius: 4px;
|
||||
|
||||
&:not(:checked) {
|
||||
&::after {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
background-color: var(--color-dark-2);
|
||||
border-color: var(--color-dark-2);
|
||||
|
||||
& + .todo-item_label {
|
||||
color: var(--color-gray-4);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
translate: -50% -50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url('/src/icons/Check.svg') center/contain no-repeat;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.todo-item_label {
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.todo_item_delete-button {
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-left: auto;
|
||||
padding: 0;
|
||||
color: var(--color-gray-4);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: inherit;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-dark-1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
scale: 1.05;
|
||||
}
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
61
src/styles/components/todo.css
Normal file
61
src/styles/components/todo.css
Normal file
@@ -0,0 +1,61 @@
|
||||
.todo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 24px;
|
||||
width: 100%;
|
||||
max-width: 404px;
|
||||
min-height: 429px;
|
||||
padding: 24px;
|
||||
background-color: var(--color-white);
|
||||
border: var(--border);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
.todo_title {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
font-weight: 600;
|
||||
letter-spacing: -2%;
|
||||
}
|
||||
.todo_form {
|
||||
display: flex;
|
||||
column-gap: 16px;
|
||||
}
|
||||
.todo_field {
|
||||
width: 100%;
|
||||
}
|
||||
.todo_info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.todo_delete-all-button {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
&:hover {
|
||||
color: var(--color-gray-4);
|
||||
}
|
||||
&:not(.is-visiable) {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
.todo_list, .todo_empty-message {
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.todo_list {
|
||||
--todoListRowGap: 8px;
|
||||
|
||||
display: grid;
|
||||
row-gap: var(--todoListRowGap);
|
||||
}
|
||||
.todo_empty-message {
|
||||
text-align: center;
|
||||
color: var(--color-gray-4);
|
||||
}
|
||||
1
src/styles/fonts.css
Normal file
1
src/styles/fonts.css
Normal file
@@ -0,0 +1 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap');
|
||||
17
src/styles/globals.css
Normal file
17
src/styles/globals.css
Normal file
@@ -0,0 +1,17 @@
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
min-height: 100vh;
|
||||
padding: 100px 30px;
|
||||
font-family: "Inter", sans-serif;
|
||||
background-color: var(--color-gray-1);
|
||||
}
|
||||
input, label, button {
|
||||
transition-duration: var(--transition-duration);
|
||||
}
|
||||
:focus-visible {
|
||||
outline: 2px dashed var(--color-black);
|
||||
outline-offset: 4px;
|
||||
transition-duration: 0s !important;
|
||||
}
|
||||
9
src/styles/index.css
Normal file
9
src/styles/index.css
Normal file
@@ -0,0 +1,9 @@
|
||||
@import "normalize.css";
|
||||
@import "fonts.css";
|
||||
@import "veriables.css";
|
||||
@import "globals.css";
|
||||
|
||||
@import "components/button.css";
|
||||
@import "components/field.css";
|
||||
@import "components/todo-item.css";
|
||||
@import "components/todo.css";
|
||||
210
src/styles/normalize.css
vendored
Normal file
210
src/styles/normalize.css
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
Нормализация блочной модели
|
||||
*/
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/**
|
||||
Убираем внутренние отступы слева тегам списков,
|
||||
у которых есть атрибут class
|
||||
*/
|
||||
:where(ul, ol):where([class]) {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Убираем внешние отступы body и двум другим тегам,
|
||||
у которых есть атрибут class
|
||||
*/
|
||||
body,
|
||||
:where(blockquote, figure, fieldset):where([class]) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Убираем внешние отступы вертикали нужным тегам,
|
||||
у которых есть атрибут class
|
||||
*/
|
||||
:where(
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
dl
|
||||
):where([class]) {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
:where(dd[class]) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:where(fieldset[class]) {
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/**
|
||||
Убираем стандартный маркер маркированному списку,
|
||||
у которого есть атрибут class
|
||||
*/
|
||||
:where(ul[class]) {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
:where(address[class]) {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
Обнуляем вертикальные внешние отступы параграфа,
|
||||
объявляем локальную переменную для внешнего отступа вниз,
|
||||
чтобы избежать взаимодействие с более сложным селектором
|
||||
*/
|
||||
p {
|
||||
--paragraphMarginBottom: 24px;
|
||||
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Внешний отступ вниз для параграфа без атрибута class,
|
||||
который расположен не последним среди своих соседних элементов
|
||||
*/
|
||||
p:where(:not([class]):not(:last-child)) {
|
||||
margin-bottom: var(--paragraphMarginBottom);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Упрощаем работу с изображениями и видео
|
||||
*/
|
||||
img,
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
Наследуем свойства шрифт для полей ввода
|
||||
*/
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
html {
|
||||
/**
|
||||
Пригодится в большинстве ситуаций
|
||||
(когда, например, нужно будет "прижать" футер к низу сайта)
|
||||
*/
|
||||
height: 100%;
|
||||
/**
|
||||
Убираем скачок интерфейса по горизонтали
|
||||
при появлении / исчезновении скроллбара
|
||||
*/
|
||||
scrollbar-gutter: stable;
|
||||
/**
|
||||
Плавный скролл
|
||||
*/
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
/**
|
||||
Пригодится в большинстве ситуаций
|
||||
(когда, например, нужно будет "прижать" футер к низу сайта)
|
||||
*/
|
||||
min-height: 100%;
|
||||
/**
|
||||
Унифицированный интерлиньяж
|
||||
*/
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/**
|
||||
Нормализация высоты элемента ссылки при его инспектировании в DevTools
|
||||
*/
|
||||
a:where([class]) {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
/**
|
||||
Курсор-рука при наведении на элемент
|
||||
*/
|
||||
button,
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
Убирает серую подсветку при тапе на мобильных устройствах (iOS/Android)
|
||||
*/
|
||||
button {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
Приводим к единому цвету svg-элементы
|
||||
(за исключением тех, у которых уже указан
|
||||
атрибут fill со значением 'none' или начинается с 'url')
|
||||
*/
|
||||
:where([fill]:not(
|
||||
[fill="none"],
|
||||
[fill^="url"]
|
||||
)) {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
/**
|
||||
Приводим к единому цвету svg-элементы
|
||||
(за исключением тех, у которых уже указан
|
||||
атрибут stroke со значением 'none')
|
||||
*/
|
||||
:where([stroke]:not(
|
||||
[stroke="none"],
|
||||
[stroke^="url"]
|
||||
)) {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
/**
|
||||
Чиним баг задержки смены цвета при взаимодействии с svg-элементами
|
||||
*/
|
||||
svg * {
|
||||
transition-property: fill, stroke;
|
||||
}
|
||||
|
||||
/**
|
||||
Приведение рамок таблиц в классический 'collapse' вид
|
||||
*/
|
||||
:where(table) {
|
||||
border-collapse: collapse;
|
||||
border-color: currentColor;
|
||||
}
|
||||
|
||||
/**
|
||||
Удаляем все анимации и переходы для людей,
|
||||
которые предпочитают их не использовать
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
21
src/styles/veriables.css
Normal file
21
src/styles/veriables.css
Normal file
@@ -0,0 +1,21 @@
|
||||
:root {
|
||||
--color-white: #ffffff;
|
||||
--color-black: #000000;
|
||||
|
||||
--color-dark-1: #1E1E1E;
|
||||
--color-dark-2: #2C2C2C;
|
||||
|
||||
--color-gray-1: #F5F5F5;
|
||||
--color-gray-2: #D9D9D9;
|
||||
--color-gray-3: #B3B3B3;
|
||||
--color-gray-4: #757575;
|
||||
|
||||
--border: 1px solid var(--color-gray-2);
|
||||
--border-radius: 8px;
|
||||
|
||||
--box-shadow: 0px 4px 4px -4px rgba(12, 12, 13, 0.05), 0px 16px 32px -4px rgba(12, 12, 13, 0.1);
|
||||
|
||||
--input-height: 40px;
|
||||
|
||||
--transition-duration: 0.2s
|
||||
}
|
||||
Reference in New Issue
Block a user