FRONTEND NOTES / 02MỚI
uiverse-io / galaxy

Giao diện đẹp,
chạm là mê.

Một chiếc nút biết tỏa sáng.
Một đường viền có nhịp điệu.
Và một vũ trụ ý tưởng mang tên Galaxy.

ts4m · /9 phút đọc & nghịch
hello, little universe
A LITTLE CHANGE
ngày ↔ đêm
GO ON, TRY ME
hover có chút bất ngờ ✦
keep the
good stuff.
CUỘN XUỐNG ĐỂ BẮT ĐẦU KHÁM PHÁ HTML + CSS · COMMUNITY-MADE · MIT

Frontend không chỉ
có “chạy được”.

Nó còn là cảm giác khi bạn đưa chuột lên một chiếc nút, gạt một công tắc, hay thấy giao diện đáp lại đúng khoảnh khắc mình chạm vào.

Ở phía frontend, HTML tạo cấu trúc, CSS định hình diện mạo và JavaScript nối các tương tác với hành vi của ứng dụng. Một chi tiết nhỏ được làm kỹ có thể khiến cả giao diện trở nên đáng nhớ. Sáu ví dụ trong bài bắt đầu từ phần rất thú vị ấy: CSS biến những hình khối đơn giản thành cảm giác.

Trong bài này, mình chọn sáu mẫu dùng HTML và CSS thuần. Chúng được đưa vào chính trang bạn đang đọc, rồi bổ sung nhãn dễ hiểu, focus bàn phím và chế độ giảm chuyển động. Bạn có thể xem phần thay đổi dưới từng demo.

Một chút lung linh.
Rất nhiều dòng CSS hay.

Chọn một thành phần. Thử tương tác.
Mở mã nguồn và xem phép màu bắt đầu ở đâu.

COMPONENT 01 / BUTTON

Nút tia sáng

Xem bản gốc ↗
Nhấn để thử · hoặc focus bằng Tab

Hover, chạm hoặc dùng Tab: nền tím sáng lên, các tia sao chuyển động quanh chữ.

box-shadow · SVG · :focus-visible

Gốc: MuhammadHasann / Uiverse

mã chạy trong demo
component.html
<button type="button" class="gx-sparkle-button-button">
  <div class="gx-sparkle-button-dots_border"></div>
  <svg aria-hidden="true" focusable="false"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    class="gx-sparkle-button-sparkle"
  >
    <path
      class="gx-sparkle-button-path"
      stroke-linejoin="round"
      stroke-linecap="round"
      stroke="black"
      fill="black"
      d="M14.187 8.096L15 5.25L15.813 8.096C16.0231 8.83114 16.4171 9.50062 16.9577 10.0413C17.4984 10.5819 18.1679 10.9759 18.903 11.186L21.75 12L18.904 12.813C18.1689 13.0231 17.4994 13.4171 16.9587 13.9577C16.4181 14.4984 16.0241 15.1679 15.814 15.903L15 18.75L14.187 15.904C13.9769 15.1689 13.5829 14.4994 13.0423 13.9587C12.5016 13.4181 11.8321 13.0241 11.097 12.814L8.25 12L11.096 11.187C11.8311 10.9769 12.5006 10.5829 13.0413 10.0423C13.5819 9.50162 13.9759 8.83214 14.186 8.097L14.187 8.096Z"
    ></path>
    <path
      class="gx-sparkle-button-path"
      stroke-linejoin="round"
      stroke-linecap="round"
      stroke="black"
      fill="black"
      d="M6 14.25L5.741 15.285C5.59267 15.8785 5.28579 16.4206 4.85319 16.8532C4.42059 17.2858 3.87853 17.5927 3.285 17.741L2.25 18L3.285 18.259C3.87853 18.4073 4.42059 18.7142 4.85319 19.1468C5.28579 19.5794 5.59267 20.1215 5.741 20.715L6 21.75L6.259 20.715C6.40725 20.1216 6.71398 19.5796 7.14639 19.147C7.5788 18.7144 8.12065 18.4075 8.714 18.259L9.75 18L8.714 17.741C8.12065 17.5925 7.5788 17.2856 7.14639 16.853C6.71398 16.4204 6.40725 15.8784 6.259 15.285L6 14.25Z"
    ></path>
    <path
      class="gx-sparkle-button-path"
      stroke-linejoin="round"
      stroke-linecap="round"
      stroke="black"
      fill="black"
      d="M6.5 4L6.303 4.5915C6.24777 4.75718 6.15472 4.90774 6.03123 5.03123C5.90774 5.15472 5.75718 5.24777 5.5915 5.303L5 5.5L5.5915 5.697C5.75718 5.75223 5.90774 5.84528 6.03123 5.96877C6.15472 6.09226 6.24777 6.24282 6.303 6.4085L6.5 7L6.697 6.4085C6.75223 6.24282 6.84528 6.09226 6.96877 5.96877C7.09226 5.84528 7.24282 5.75223 7.4085 5.697L8 5.5L7.4085 5.303C7.24282 5.24777 7.09226 5.15472 6.96877 5.03123C6.84528 4.90774 6.75223 4.75718 6.697 4.5915L6.5 4Z"
    ></path>
  </svg>
  <span class="gx-sparkle-button-text_button">Chạm thử nhé</span>
</button>

<!-- Source: https://github.com/uiverse-io/galaxy/blob/adbd2adde0a299a3956ea288fb444ec01891ca41/Buttons/MuhammadHasann_mean-bird-62.html
Author: MuhammadHasann / Uiverse.io
Adapted for ts4m.dev.
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
component.css
/* From Uiverse.io by MuhammadHasann  - Website: https://twitter.com/jh3yy/status/1639001228985225228 - Name: Jhey - Tags: simple, neumorphism, button */
.gx-sparkle-button-button {
  --black-700: hsla(0 0% 12% / 1);
  --border_radius: 9999px;
  --transtion: 0.3s ease-in-out;
  --offset: 2px;

  cursor: pointer;
  position: relative;

  display: flex;
  align-items: center;
  gap: 0.5rem;

  transform-origin: center;

  padding: 1rem 2rem;
  background-color: transparent;

  border: none;
  border-radius: var(--border_radius);
  transform: scale(calc(1 + (var(--active, 0) * 0.1)));

  transition: transform var(--transtion);
}

.gx-sparkle-button-button::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  width: 100%;
  height: 100%;
  background-color: var(--black-700);

  border-radius: var(--border_radius);
  box-shadow: inset 0 0.5px hsl(0, 0%, 100%), inset 0 -1px 2px 0 hsl(0, 0%, 0%),
    0px 4px 10px -4px hsla(0 0% 0% / calc(1 - var(--active, 0))),
    0 0 0 calc(var(--active, 0) * 0.375rem) hsl(260 97% 50% / 0.75);

  transition: all var(--transtion);
  z-index: 0;
}

.gx-sparkle-button-button::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  width: 100%;
  height: 100%;
  background-color: hsla(260 97% 61% / 0.75);
  background-image: radial-gradient(
      at 51% 89%,
      hsla(266, 45%, 74%, 1) 0px,
      transparent 50%
    ),
    radial-gradient(at 100% 100%, hsla(266, 36%, 60%, 1) 0px, transparent 50%),
    radial-gradient(at 22% 91%, hsla(266, 36%, 60%, 1) 0px, transparent 50%);
  background-position: top;

  opacity: var(--active, 0);
  border-radius: var(--border_radius);
  transition: opacity var(--transtion);
  z-index: 2;
}

.gx-sparkle-button-button:is(:hover, :focus-visible) {
  --active: 1;
}
.gx-sparkle-button-button:active {
  transform: scale(1);
}

.gx-sparkle-button-button .gx-sparkle-button-dots_border {
  --size_border: calc(100% + 2px);

  overflow: hidden;

  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  width: var(--size_border);
  height: var(--size_border);
  background-color: transparent;

  border-radius: var(--border_radius);
  z-index: -10;
}

.gx-sparkle-button-button .gx-sparkle-button-dots_border::before {
  content: "";
  position: absolute;
  top: 30%;
  left: 50%;
  transform: translate(-50%, -50%);
  transform-origin: left;
  transform: rotate(0deg);

  width: 100%;
  height: 2rem;
  background-color: white;

  mask: linear-gradient(transparent 0%, white 120%);
  animation: gx-sparkle-button-rotate 2s linear infinite;
}

@keyframes gx-sparkle-button-rotate {
  to {
    transform: rotate(360deg);
  }
}

.gx-sparkle-button-button .gx-sparkle-button-sparkle {
  position: relative;
  z-index: 10;

  width: 1.75rem;
}

.gx-sparkle-button-button .gx-sparkle-button-sparkle .gx-sparkle-button-path {
  fill: currentColor;
  stroke: currentColor;

  transform-origin: center;

  color: hsl(0, 0%, 100%);
}

.gx-sparkle-button-button:is(:hover, :focus) .gx-sparkle-button-sparkle .gx-sparkle-button-path {
  animation: gx-sparkle-button-path 1.5s linear 0.5s infinite;
}

.gx-sparkle-button-button .gx-sparkle-button-sparkle .gx-sparkle-button-path:nth-child(1) {
  --scale_path_1: 1.2;
}
.gx-sparkle-button-button .gx-sparkle-button-sparkle .gx-sparkle-button-path:nth-child(2) {
  --scale_path_2: 1.2;
}
.gx-sparkle-button-button .gx-sparkle-button-sparkle .gx-sparkle-button-path:nth-child(3) {
  --scale_path_3: 1.2;
}

@keyframes gx-sparkle-button-path {
  0%,
  34%,
  71%,
  100% {
    transform: scale(1);
  }
  17% {
    transform: scale(var(--scale_path_1, 1));
  }
  49% {
    transform: scale(var(--scale_path_2, 1));
  }
  83% {
    transform: scale(var(--scale_path_3, 1));
  }
}

.gx-sparkle-button-button .gx-sparkle-button-text_button {
  position: relative;
  z-index: 10;

  background-image: linear-gradient(
    90deg,
    hsla(0 0% 100% / 1) 0%,
    hsla(0 0% 100% / var(--active, 0)) 120%
  );
  background-clip: text;

  font-size: 1rem;
  color: transparent;
}
.gx-sparkle-button-button{font:inherit;isolation:isolate}
.gx-sparkle-button-button .gx-sparkle-button-text_button{color:#fff;background-image:none;font-size:14px;font-weight:650}

/* ts4m: respect reduced motion. */
@media(prefers-reduced-motion:reduce){
 html:not(.allow-motion) [class^="gx-sparkle-button"], html:not(.allow-motion) [class^="gx-sparkle-button"] *, html:not(.allow-motion) [class^="gx-sparkle-button"]::before, html:not(.allow-motion) [class^="gx-sparkle-button"]::after, html:not(.allow-motion) [class^="gx-sparkle-button"] *::before, html:not(.allow-motion) [class^="gx-sparkle-button"] *::after{animation:none!important;transition:none!important}
}

/*
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
Tải .html ↓

Bản trong bài: Giữ hiệu ứng gốc, thêm nhãn tiếng Việt và kiểu button rõ ràng. Tên class được thêm tiền tố để hạn chế đụng CSS; mã có kèm giấy phép MIT.

COMPONENT 02 / CARD

Viền có nhịp điệu

Xem bản gốc ↗

Make it
feel alive.

MADE OF PURE CSS
Hover hoặc dùng Tab để thấy viền đổi màu

Một đường viền đổi sắc ôm lấy nội dung. Thử rê chuột vào card hoặc đưa focus tới nó.

conic-gradient · mask-composite

Gốc: Clenio21 / Uiverse

mã chạy trong demo
component.html
<div class="gx-gradient-card-card" tabindex="0" role="group" aria-label="Card viền cầu vồng, thử hover hoặc focus"><span class="card-star" aria-hidden="true">✦</span><p>Make it<br><strong>feel alive.</strong></p><small>MADE OF PURE CSS</small></div>

<!-- Source: https://github.com/uiverse-io/galaxy/blob/adbd2adde0a299a3956ea288fb444ec01891ca41/Cards/Clenio21_stale-sloth-57.html
Author: Clenio21 / Uiverse.io
Adapted for ts4m.dev.
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
component.css
/* From Uiverse.io by Clenio21 - Tags: card */
.gx-gradient-card-card {
 width: 190px;
 height: 254px;
 --border-radius: 15px;
 --border-width: 4px;
 appearance: none;
 position: relative;
 padding: 1em 2em;
 border: 0;
 background: rgb(5, 5, 22);
 font-size: 18px;
 font-weight: 500;
 color: #fff;
 z-index: 2;
 border-radius: 15px;
}

.gx-gradient-card-card::after {
 --m-i: linear-gradient(#000, #000);
 --m-o: content-box, padding-box;
 content: "";
 position: absolute;
 left: 0;
 top: 0;
 width: 100%;
 height: 100%;
 padding: var(--border-width);
 border-radius: var(--border-radius);
 background-image: conic-gradient(
		#488cfb,
		#29dbbc,
		#ddf505,
		#ff9f0e,
		#e440bb,
		#655adc,
		#488cfb
	);
 -webkit-mask-image: var(--m-i), var(--m-i);
 mask-image: var(--m-i), var(--m-i);
 -webkit-mask-origin: var(--m-o);
 mask-origin: var(--m-o);
 -webkit-mask-clip: var(--m-o);
 mask-composite: exclude;
 -webkit-mask-composite: destination-out;
 filter: hue-rotate(0);
 animation: gx-gradient-card-rotate-hue linear 4s infinite;
 animation-play-state: paused;
}

.gx-gradient-card-card:is(:hover,:focus-visible)::after {
 animation-play-state: running;
}

@keyframes gx-gradient-card-rotate-hue {
 to {
  filter: hue-rotate(1turn);
 }
}

.gx-gradient-card-card,
.gx-gradient-card-card::after {
 box-sizing: border-box;
}

.gx-gradient-card-card:active {
 --border-width: 5px;
}
.gx-gradient-card-card{width:230px;max-width:calc(100vw - 80px);height:280px;padding:30px 25px;display:flex;flex-direction:column;justify-content:space-between;text-align:left;font-family:inherit}
.gx-gradient-card-card::after{pointer-events:none}
.gx-gradient-card-card p{font-size:28px;line-height:1.15;letter-spacing:-1px;margin:22px 0}.gx-gradient-card-card strong{color:#c5b3ff}.gx-gradient-card-card .card-star{font-size:35px;color:#d4c4ff}.gx-gradient-card-card small{font-size:9px;letter-spacing:1.5px;color:#c8c8d7}

/* ts4m: respect reduced motion. */
@media(prefers-reduced-motion:reduce){
 html:not(.allow-motion) [class^="gx-gradient-card"], html:not(.allow-motion) [class^="gx-gradient-card"] *, html:not(.allow-motion) [class^="gx-gradient-card"]::before, html:not(.allow-motion) [class^="gx-gradient-card"]::after, html:not(.allow-motion) [class^="gx-gradient-card"] *::before, html:not(.allow-motion) [class^="gx-gradient-card"] *::after{animation:none!important;transition:none!important}
}

/*
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
Tải .html ↓

Bản trong bài: Thêm nội dung, hỗ trợ focus bàn phím và làm chậm vòng đổi màu. Tên class được thêm tiền tố để hạn chế đụng CSS; mã có kèm giấy phép MIT.

COMPONENT 03 / SWITCH

Ngày, rồi lại đêm

Xem bản gốc ↗
Gạt công tắc để đổi nền khung demo

Mặt trời trượt thành trăng khuyết. Gạt thử để đổi nền ngay trong khung demo.

pseudo-element · :checked

Gốc: satyamchaudharydev / Uiverse

mã chạy trong demo
component.html
<label class="gx-theme-switch-switch">
    <input type="checkbox" aria-label="Đổi nền demo sang ban đêm">
    <span aria-hidden="true" class="gx-theme-switch-slider"></span>
</label>

<!-- Source: https://github.com/uiverse-io/galaxy/blob/adbd2adde0a299a3956ea288fb444ec01891ca41/Toggle-switches/satyamchaudharydev_shy-earwig-18.html
Author: satyamchaudharydev / Uiverse.io
Adapted for ts4m.dev.
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
component.css
/* From Uiverse.io by satyamchaudharydev - Tags: switch, dark, theme, light, theme-switch */
/* The switch - the box around the slider */
.gx-theme-switch-switch {
  display: block;
  --width-of-switch: 3.5em;
  --height-of-switch: 2em;
  /* size of sliding icon -- sun and moon */
  --size-of-icon: 1.4em;
  /* it is like a inline-padding of switch */
  --slider-offset: 0.3em;
  position: relative;
  width: var(--width-of-switch);
  height: var(--height-of-switch);
}

/* Hide default HTML checkbox */
.gx-theme-switch-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

/* The slider */
.gx-theme-switch-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #f4f4f5;
  transition: .4s;
  border-radius: 30px;
}

.gx-theme-switch-slider:before {
  position: absolute;
  content: "";
  height: var(--size-of-icon,1.4em);
  width: var(--size-of-icon,1.4em);
  border-radius: 20px;
  left: var(--slider-offset,0.3em);
  top: 50%;
  transform: translateY(-50%);
  background: linear-gradient(40deg,#ff0080,#ff8c00 70%);
  ;
 transition: .4s;
}

.gx-theme-switch-switch input:checked + .gx-theme-switch-slider {
  background-color: #303136;
}

.gx-theme-switch-switch input:checked + .gx-theme-switch-slider:before {
  left: calc(100% - (var(--size-of-icon,1.4em) + var(--slider-offset,0.3em)));
  background: #303136;
  /* change the value of second inset in box-shadow to change the angle and direction of the moon  */
  box-shadow: inset -3px -2px 5px -2px #8983f7, inset -10px -4px 0 0 #a3dafb;
}
.gx-theme-switch-switch{font-size:22px;min-height:44px}.gx-theme-switch-switch input:focus-visible+.gx-theme-switch-slider{outline:3px solid #7451c7;outline-offset:5px}

/* ts4m: respect reduced motion. */
@media(prefers-reduced-motion:reduce){
 html:not(.allow-motion) [class^="gx-theme-switch"], html:not(.allow-motion) [class^="gx-theme-switch"] *, html:not(.allow-motion) [class^="gx-theme-switch"]::before, html:not(.allow-motion) [class^="gx-theme-switch"]::after, html:not(.allow-motion) [class^="gx-theme-switch"] *::before, html:not(.allow-motion) [class^="gx-theme-switch"] *::after{animation:none!important;transition:none!important}
}

/*
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
Tải .html ↓

Bản trong bài: Thêm nhãn cho checkbox và viền focus. Màu nền demo là phần tích hợp của bài. Tên class được thêm tiền tố để hạn chế đụng CSS; mã có kèm giấy phép MIT.

COMPONENT 05 / LOADER

Sáu mặt. Một chuyển động.

Xem bản gốc ↗
Demo chuyển động CSS · không có tác vụ đang tải

Chỉ sáu thẻ div, transform 3D và keyframes. Khối kính này là bản trình diễn, không báo một tác vụ đang tải.

preserve-3d · rotateX · rotateY

Gốc: AqFox / Uiverse

mã chạy trong demo
component.html
<div role="img" aria-label="Demo: khối kính lập phương xoay"><div class="gx-glass-cube-spinner">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div></div>

<!-- Source: https://github.com/uiverse-io/galaxy/blob/adbd2adde0a299a3956ea288fb444ec01891ca41/loaders/AqFox_young-dragon-29.html
Author: AqFox / Uiverse.io
Adapted for ts4m.dev.
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
component.css
/* From Uiverse.io by AqFox - Tags: 3d, loader, cube */
.gx-glass-cube-spinner {
 width: 44px;
 height: 44px;
 animation: gx-glass-cube-spinner-y0fdc1 6s infinite ease;
 transform-style: preserve-3d;
}

.gx-glass-cube-spinner > div {
 background-color: rgba(0,77,255,0.2);
 height: 100%;
 position: absolute;
 width: 100%;
 border: 2px solid #004dff;
}

.gx-glass-cube-spinner div:nth-of-type(1) {
 transform: translateZ(-22px) rotateY(180deg);
}

.gx-glass-cube-spinner div:nth-of-type(2) {
 transform: rotateY(-270deg) translateX(50%);
 transform-origin: top right;
}

.gx-glass-cube-spinner div:nth-of-type(3) {
 transform: rotateY(270deg) translateX(-50%);
 transform-origin: center left;
}

.gx-glass-cube-spinner div:nth-of-type(4) {
 transform: rotateX(90deg) translateY(-50%);
 transform-origin: top center;
}

.gx-glass-cube-spinner div:nth-of-type(5) {
 transform: rotateX(-90deg) translateY(50%);
 transform-origin: bottom center;
}

.gx-glass-cube-spinner div:nth-of-type(6) {
 transform: translateZ(22px);
}

@keyframes gx-glass-cube-spinner-y0fdc1 {
 0% {
  transform: rotate(45deg) rotateX(-25deg) rotateY(25deg);
 }

 50% {
  transform: rotate(45deg) rotateX(-385deg) rotateY(25deg);
 }

 100% {
  transform: rotate(45deg) rotateX(-385deg) rotateY(385deg);
 }
}
/* ts4m: respect reduced motion. */
@media(prefers-reduced-motion:reduce){
 html:not(.allow-motion) [class^="gx-glass-cube"], html:not(.allow-motion) [class^="gx-glass-cube"] *, html:not(.allow-motion) [class^="gx-glass-cube"]::before, html:not(.allow-motion) [class^="gx-glass-cube"]::after, html:not(.allow-motion) [class^="gx-glass-cube"] *::before, html:not(.allow-motion) [class^="gx-glass-cube"] *::after{animation:none!important;transition:none!important}
}

/*
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

.gx-glass-cube-spinner{transform:rotate(45deg) rotateX(-25deg) rotateY(25deg)}
Tải .html ↓

Bản trong bài: Giữ cấu trúc sáu mặt, làm chậm chuyển động và hỗ trợ giảm chuyển động. Tên class được thêm tiền tố để hạn chế đụng CSS; mã có kèm giấy phép MIT.

COMPONENT 06 / CHECKBOX

Ghim một chút cảm hứng

Xem bản gốc ↗
Nhấn Space hoặc bấm biểu tượng để ghim

Bấm hoặc nhấn Space để ghim. Trạng thái checkbox hiển thị ngay trên biểu tượng và giữ trong phiên đọc.

:checked · SVG · transform

Gốc: vinodjangid07 / Uiverse

mã chạy trong demo
component.html
<label class="gx-bookmark-checkbox-container">
  <input type="checkbox" aria-label="Ghim cảm hứng" />
  <svg aria-hidden="true" focusable="false"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 75 100"
    class="gx-bookmark-checkbox-pin"
  >
    <line
      stroke-width="12"
      stroke="black"
      y2="100"
      x2="37"
      y1="64"
      x1="37"
    ></line>
    <path
      stroke-width="10"
      stroke="black"
      d="M16.5 36V4.5H58.5V36V53.75V54.9752L59.1862 55.9903L66.9674 67.5H8.03256L15.8138 55.9903L16.5 54.9752V53.75V36Z"
    ></path>
  </svg>
</label>

<!-- Source: https://github.com/uiverse-io/galaxy/blob/adbd2adde0a299a3956ea288fb444ec01891ca41/Checkboxes/vinodjangid07_sour-rabbit-84.html
Author: vinodjangid07 / Uiverse.io
Adapted for ts4m.dev.
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
component.css
/* From Uiverse.io by vinodjangid07  - Tags: hover, checkbox, toggle, save, hover effect, pin, bookmark */
.gx-bookmark-checkbox-container input {
  position:absolute;opacity:0;width:1px;height:1px;
}
.gx-bookmark-checkbox-pin {
  width: 15px;
  height: auto;
  transform: rotate(35deg);
}
.gx-bookmark-checkbox-container {
  width: 45px;
  height: 45px;
  background-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 10px;
}
.gx-bookmark-checkbox-container input:checked ~ .gx-bookmark-checkbox-pin {
  fill: rgb(179, 139, 255);
  stroke: white;
}
.gx-bookmark-checkbox-container input:checked ~ .gx-bookmark-checkbox-pin line {
  stroke: rgb(179, 139, 255);
}
.gx-bookmark-checkbox-container input:checked ~ .gx-bookmark-checkbox-pin path {
  stroke: rgb(179, 139, 255);
}
.gx-bookmark-checkbox-container:hover {
  background-color: rgb(244, 238, 255);
  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.041);
}
.gx-bookmark-checkbox-container:active {
  transform: scale(0.9);
}
.gx-bookmark-checkbox-container{position:relative;width:52px;height:52px;background:#fff;border:1px solid #e0d6f2}
.gx-bookmark-checkbox-container input:focus-visible~.gx-bookmark-checkbox-pin{outline:3px solid #7655ba;outline-offset:8px}

/* ts4m: respect reduced motion. */
@media(prefers-reduced-motion:reduce){
 html:not(.allow-motion) [class^="gx-bookmark-checkbox"], html:not(.allow-motion) [class^="gx-bookmark-checkbox"] *, html:not(.allow-motion) [class^="gx-bookmark-checkbox"]::before, html:not(.allow-motion) [class^="gx-bookmark-checkbox"]::after, html:not(.allow-motion) [class^="gx-bookmark-checkbox"] *::before, html:not(.allow-motion) [class^="gx-bookmark-checkbox"] *::after{animation:none!important;transition:none!important}
}

/*
MIT License

Copyright (c) 2023 Uiverse.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
Tải .html ↓

Bản trong bài: Giữ input trong luồng focus, thêm tên dễ hiểu và vùng bấm tối thiểu 44 px. Tên class được thêm tiền tố để hạn chế đụng CSS; mã có kèm giấy phép MIT.

6 demo thật · Không cần cài thư viện để thửTải cả bộ HTML/CSS + giấy phép ↓

Đẹp có lý do.
Không phải ngẫu nhiên.

Điều đáng lấy về không chỉ là đoạn mã.
Còn là cách tác giả giải một bài toán thị giác.

Xếp lớp để có chiều sâu.

Nút tia sáng dùng lớp nền, viền và SVG tách biệt. Pseudo-element giúp tạo ánh sáng mà không cần thêm ảnh. Hãy đọc thứ tự các lớp trước khi chỉnh màu: một z-index đặt sai cũng đủ làm hiệu ứng biến mất.

::before / ::after

Một gradient, nhiều sắc độ.

Card của Clenio21 dùng conic-gradient() cùng lớp mask để chỉ giữ phần viền. Đổi màu không nhất thiết phải đổi cả khối nền; một đường sáng mỏng đôi khi đã đủ kéo mắt về nội dung.

conic-gradient / mask

Nhịp chuyển động tạo cảm giác.

Cùng một khối kính, tốc độ khác nhau sẽ tạo cảm giác khác nhau. Bản trong bài làm chậm vòng quay để dễ ngắm. Hãy cho chuyển động một mục đích, và để người đọc có quyền tắt nó.

transform / keyframes

Copy là bắt đầu.
Remix mới là bạn.

Mượn một ý tưởng đẹp, rồi làm cho nó hợp với sản phẩm của mình. Một giao diện tốt vẫn cần dễ đọc, dễ dùng và phản hồi đúng.

const goodUI = {
  personality: true,
  clarity: true,
  aLittleMagic: "✦"
};
Một chút cá tính. Đủ nhiều rõ ràng.
  1. 01

    Mở một bản chạy được trước.

    Bấm “Copy bản chạy ngay”, lưu thành demo.html rồi mở bằng trình duyệt. File đã có HTML, CSS và thông tin nguồn. Không cần dựng một dự án mới chỉ để ngắm một chiếc nút.

  2. 02

    Tách mã, chỉnh cho hợp nhà.

    Khi đưa vào website có sẵn, copy HTML và CSS ở hai tab riêng. Giữ tên class khớp nhau. Bản trong bài dùng tiền tố gx- để hạn chế đụng với các tên quen thuộc như .button hoặc .card.

    Nếu dùng React, bạn cần đổi các thuộc tính HTML/SVG sang cú pháp JSX tương ứng. Nếu chọn một mẫu Tailwind khác trong repo, hãy kiểm tra dự án đã có cách xử lý các utility class đó.

  3. 03

    Gắn hành vi thật cho giao diện.

    Một switch đẹp chưa tự đổi toàn bộ giao diện ứng dụng. Một input phát sáng chưa tự tìm kiếm dữ liệu. Dùng trạng thái của ứng dụng để nối phần nhìn với hành vi bạn muốn, rồi thử cả chuột lẫn bàn phím.

  4. 04

    Giữ lại nguồn và sự dễ chịu.

    Giữ thông báo bản quyền, giấy phép trong mã được phân phối và ghi credit khi có thể. Thử màn hình nhỏ, kiểm tra chữ dễ đọc, thêm prefers-reduced-motion. Nút “Hiệu ứng” trên trang này cũng dừng các chuyển động trong demo.

Trước khi
git commit.

Có cần cài npm package để dùng sáu mẫu này không?

Không. Sáu mẫu được chọn trong bài dùng HTML, CSS và SVG nội tuyến. Bản “chạy ngay” mở được như một file HTML. Những mẫu khác trong Galaxy có thể dùng Tailwind; xem mã gốc để biết chúng cần gì.

Đoạn mã được copy có đúng với demo không?

Có. Demo và khối mã được tạo từ cùng một bản HTML/CSS đã điều chỉnh. Bên dưới có mô tả thay đổi và link tới bản gốc cố định trên GitHub. Phần khung trang trí của bài viết không nằm trong snippet của component.

Dùng trong dự án thương mại được không?

Repo công bố các thành phần theo giấy phép MIT. Bạn có thể đọc nội dung giấy phép gốc; bản mã và gói tải ở đây đã kèm thông báo bản quyền, điều khoản cho phép sử dụng và phân phối.

Làm sao đóng góp một component cho Galaxy?

README hướng dẫn gửi thiết kế trên Uiverse. Sau khi được duyệt ở nền tảng, thành phần sẽ được đưa vào repo. Vì vậy, hãy xem hướng dẫn hiện hành của Uiverse trước khi gửi đóng góp.

Ánh sáng đến từ
nhiều bàn tay.

Sáu component gốc thuộc về các tác giả bên cạnh. ts4m tuyển chọn, điều chỉnh và thiết kế bài viết này. Riêng nút của MuhammadHasann còn giữ attribution tới Jhey trong comment nguồn.

Nguồn được cố định tại commit adbd2ad · MIT License

  1. 01
    Nút tia sángMuhammadHasann
    Nguồn ↗
  2. 02
    Viền có nhịp điệuClenio21
    Nguồn ↗
  3. 03
    Ngày, rồi lại đêmsatyamchaudharydev
    Nguồn ↗
  4. 04
    Một ô nhập biết phát sángLakshay-art
    Nguồn ↗
  5. 05
    Sáu mặt. Một chuyển động.AqFox
    Nguồn ↗
  6. 06
    Ghim một chút cảm hứngvinodjangid07
    Nguồn ↗

Một chút CSS.
Một chút “wow”.

Hy vọng bạn mang về được một ý tưởng muốn thử ngay.

Nghịch thêm chút nữa ↗
Đọc xong code, mình mời cơm nhà →

Sao chép mã

Trình duyệt chưa cho phép copy tự động. Mã đã được chọn để bạn sao chép.