
Security News
/Research
Fake Corepack Site Distributes Infostealer and Proxyware to Developers
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.
@huxy/materials
Advanced tools
在前端开发中,css 为我们的页面带来了生机与活力,我们可以使用它来创建各式各样的场景。如动效、形状、边框、文本等,下面简单介绍一下一些较常用的功能实现。

css 动效主要有 animation 、transition 来实现。

3D 旋转效果。
.model-container {
position: relative;
width: 100%;
height: 100%;
background-color: transparent;
transform-style: preserve-3d;
backface-visibility: hidden;
animation: huxy-3d-models var(--time) linear 0s infinite;
}
卡片翻转动效。

.huxy-card-fliper {
width: 100%;
height: 100%;
position: relative;
perspective: 1000;
}
.card{
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.3s;
}
.front, .back{
width: 100%;
height: 100%;
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
}
通过 transform 设置偏移量和大小。

const style = {transform: `translate(${item.transform.x}px, ${item.transform.y}px) scale(${item.transform.s})`};
加载中效果。

@keyframes huxy-loading-ball {
0% , 40% , 100%
{
transform: scale(0.2);
}
20%{
transform: scale(1);
}
}
.huxy-loading-ball {
display: flex;
.ball {
background: var(--bgColor);
box-shadow:0 0 var(--shadowSize) var(--bgColor);
width: var(--size);
height: var(--size);
border-radius: 50%;
animation: huxy-loading-ball var(--time) ease-in-out var(--delay) infinite;
}
}
波浪效果。
@keyframes huxy-loading-wave {
0% {
width: 0;
opacity: 0.1;
}
34% {
width: 0;
opacity: 0.5;
}
100% {
width: 100%;
opacity: 0;
}
}
.huxy-loading-wave {
position: relative;
&::after {
content: "";
position: absolute;
display: block;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: var(--waveColor);
border-radius: 2px;
opacity: 0;
animation: huxy-loading-wave var(--waveTime) ease-out infinite;
}
}
css 可绘制各种各样形状,主要使用 box-shadow 、border 、border-radius 、transform 等属性。

@keyframes huxy-timer {
100% {
transform: rotate(360deg);
}
}
.huxy-timer {
width: var(--size);
height: var(--size);
position: relative;
border-radius: 50%;
box-shadow: inset 0px 0px 0px 2px #fff;
}
.huxy-timer:after,
.huxy-timer:before {
position: absolute;
content: "";
background: var(--color);
height: var(--handWidth);
transform-origin: 1px 1px;
top: calc(var(--size) / 2 - var(--handWidth) / 2);
left: calc(var(--size) / 2 - var(--handWidth) / 2);
}
.huxy-timer:after {
width: var(--maxHeight);
animation: huxy-timer var(--maxTime) linear infinite;
}
.huxy-timer:before {
width: var(--minHeight);
animation: huxy-timer var(--minTime) linear infinite;
}
.huxy-search {
width: var(--size);
height: var(--size);
position: relative;
border-radius: 50%;
box-shadow: 0px 0px 0px 1px var(--color);
animation: huxy-search var(--time) linear infinite alternate;
}
.huxy-search:before {
position: absolute;
content: "me";
font-size: 12px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.huxy-search:after {
position: absolute;
content: "";
width: var(--bar-width);
height: var(--bar-height);
background: var(--color);
bottom: calc(var(--bar-width) - var(--bar-height));
left: calc(var(--size) - var(--bar-width) * 2);
border-radius: var(--bar-width);
transform: rotate(-45deg);
}
@keyframes huxy-location {
50% {
top: calc(50% - var(--size) * 0.25);
}
}
@keyframes huxy-location-back {
50% {
transform: translateX(-50%) scale(0.5);
}
}
.huxy-location {
position: relative;
width: var(--size);
height: var(--size);
}
.huxy-location:before,
.huxy-location:after {
position: absolute;
content: "";
}
.huxy-location:before {
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(-46deg);
width: calc(var(--size) * 0.75);
height: calc(var(--size) * 0.75);
border-radius: 100% 100% 100% 0;
box-shadow: 0px 0px 0px 2px var(--color);
animation: huxy-location var(--time) linear infinite;
}
.huxy-location:after {
left: 50%;
transform: translateX(-50%) scale(1);
bottom: -50%;
width: calc(var(--size) * 1.25);
height: calc(var(--size) * 0.5);
border-radius: 100%;
background: var(--bgColor);
animation: huxy-location-back var(--time) linear infinite;
}

@keyframes huxy-spot {
0% {
transform: scale(0.5);
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
.huxy-spot {
display: inline-block;
width: var(--size);
height: var(--size);
color: var(--color);
background: currentColor;
border-radius: 50%;
position: relative;
}
.huxy-spot:before,
.huxy-spot:after {
content: "";
position: absolute;
background: currentColor;
border-radius: 50%;
animation: huxy-spot 1.5s ease-in-out infinite;
}
.huxy-spot:before {
width: 100%;
height: 100%;
left: 0;
top: 0;
}
.huxy-spot:after {
width: 150%;
height: 150%;
left: -25%;
top: -25%;
}
常用于展开与收缩图标。

.huxy-angle
position: relative;
display: inline-block;
font-style: normal;
float: right;
cursor: pointer;
height: 100%;
width: 2 * @angleSize;
&::before,
&::after {
content: "";
display: block;
position: absolute;
border-radius: 1px;
background-color: currentColor;
height: @width;
width: @angleSize;
left: 0;
transform-origin: 100% 50% 0;
transition: all 0.3s;
}
&::before {
left: 0;
}
&::after {
left: calc(@width * 0.7);
}
}
常用于下拉框、提示框等。
.huxy-arrow
position: relative;
background: @arrowBgColor;
border: 1px solid @arrowBorderColor;
&::before,
&::after {
content: "";
position: absolute;
width: 0;
height: 0;
border: solid transparent;
}
&::before {
border-width: @arrowSize;
}
&::after {
border-width: @arrowSize - @arrowWidth;
}
}
常用于选中、active、hover等操作状态。

.huxy-follow
position: relative;
&::after {
content: "";
display: block;
position: absolute;
left: @followPadding;
bottom: 0;
top: auto;
height: @followSize;
width: calc(100% - @followPadding * 2);
background-color: @followColor;
transform: scale(0);
transition: transform 0.3s;
}
&.active {
&::after {
transform: scale(1);
transition: transform 0.3s;
}
}
}

.huxy-taiji {
box-sizing: content-box;
position: relative;
width: var(--width);
height: calc(var(--width) / 2);
background: var(--white);
border-color: var(--black);
border-style: solid;
border-width: var(--border) var(--border) calc(var(--width) / 2 + var(--border)) var(--border);
border-radius: 100%;
transform: rotate(90deg);
&::before,
&::after {
content: "";
position: absolute;
box-sizing: content-box;
width: calc(var(--width) / 8);
height: calc(var(--width) / 8);
border-radius: 100%;
top: 50%;
}
&::before {
left: 0;
background: var(--black);
border: calc(var(--width) * 3 / 16) solid var(--white);
}
&::after {
left: 50%;
background: var(--white);
border: calc(var(--width) * 3 / 16) solid var(--black);
}
}
css 可绘制一些简单图表,主要使用 border-radius 、conic-gradient 、box-shadow 等实现。

.huxy-skeleton-card {
position: relative;
height: var(--height);
background: var(--bgColor);
overflow: hidden;
.sk-card-left {
position: absolute;
left: 0;
top: 0;
width: var(--height);
height: 100%;
.sk-card-img {
width: 60%;
height: 60%;
border-radius: 50%;
background: var(--leftColor);
margin: 20% auto;
}
}
.sk-card-right {
margin-left: var(--height);
margin-top: calc(var(--height) * 0.2);
height: calc(var(--height) * 0.6);
padding: 0 10px;
position: relative;
overflow: hidden;
.title,
.text {
margin: 0;
border-radius: 4px;
background: var(--rightColor);
}
.title {
font-size: 2.2rem;
height: calc(var(--height) * 0.22);
width: var(--topWidth);
margin-top: calc(var(--height) * 0.05);
}
.text {
font-size: 1.6rem;
height: calc(var(--height) * 0.18);
width: var(--bottomWidth);
margin-top: calc(var(--height) * 0.1);
}
}
}
.huxy-progress-wave {
position: relative;
border-radius: var(--borderRadius);
height: var(--height);
background: var(--bgColor);
overflow: hidden;
color: #fff;
&::before,
&::after {
content: "";
position: absolute;
display: block;
top: 0;
left: 0;
height: 100%;
width: calc(100% * var(--percent) / 100);
max-width: 100%;
border-radius: var(--borderRadius);
background: var(--progressColor);
transition: width 1.5s ease-in;
}
&::before {
counter-reset: percent var(--percent);
content: counter(percent) "%";
text-align: right;
}
&::after {
background: var(--waveColor);
opacity: 0;
animation: huxy-progress-wave var(--waveTime) ease-out infinite;
}
}
table {
border-spacing: 0;
border-collapse: collapse;
width: 100%;
th {
padding: 15px 4%;
background: var(--thBgColor);
span {
display: block;
height: 18px;
border-radius: 2px;
background: var(--thItemColor);
}
}
td {
padding: 12px 3%;
span {
display: block;
height: 16px;
border-radius: 2px;
background: var(--tdItemColor);
}
}
tr {
transition: background 0.3s;
&:hover {
background: var(--hoverColor);
}
&:nth-of-type(odd) {
background: var(--oddColor);
}
}
}
.huxy-skeleton-barchart {
border-left: 1px solid var(--borderColor);
border-bottom: 1px solid var(--borderColor);
height: var(--height);
position: relative;
.sk-bar-item {
--itemHeight: 60%;
--bgColor: rgba(0, 0, 0, 0.1);
position: absolute;
left: 0;
bottom: 0;
display: block;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
margin-left: var(--space);
width: var(--itemWidth);
height: var(--itemHeight);
background: var(--bgColor);
box-shadow: 0 0 4px 2px rgba(255, 255, 255, 0.08);
}
}
.huxy-skeleton-piechart {
width: var(--height);
height: var(--height);
background: conic-gradient(var(--color1) 0 var(--percent1), var(--color2) var(--percent1) var(--percent2), var(--color3) var(--percent2) var(--percent3), var(--color4) var(--percent3) var(--percent4));
border-radius: 50%;
box-shadow: 0 0 6px 6px rgba(255, 255, 255, 0.08);
}
.huxy-skeleton-list {
background: var(--bgColor);
padding: 15px var(--space);
.sk-list-item {
position: relative;
border-radius: 2px;
height: var(--itemHeight);
width: calc(100% - var(--rightWidth) - var(--space));
background: var(--leftColor);
&::after {
content: "";
position: absolute;
top: 0;
right: calc(0px - var(--space) - var(--rightWidth));
border-radius: 2px;
height: var(--itemHeight);
width: var(--rightWidth);
background: var(--rightColor);
}
& + .sk-list-item {
margin-top: calc(var(--space) - 2px);
}
}
}

.huxy-skeleton-dashboard {
--size: 400px;
--width: 20px;
--space: 20px;
--color0: #fff;
--color1: transparent;
--color2: #f1462c;
--color3: #fc5d2c;
--color4: #fba73e;
--color5: #e0fa4e;
--color6: #12dd7e;
--color7: #0a6e3f;
position: relative;
margin: 0 auto;
border-radius: 50%;
width: var(--size);
height: var(--size);
background: conic-gradient(
var(--color2) 0%,
var(--color3) 12.4%,
var(--color0) 12.4%,
var(--color0) 12.5%,
var(--color3) 12.5%,
var(--color4) 24.9%,
var(--color0) 24.9%,
var(--color0) 25%,
var(--color4) 25%,
var(--color5) 37.4%,
var(--color0) 37.4%,
var(--color0) 37.5%,
var(--color5) 37.5%,
var(--color6) 49.9%,
var(--color0) 49.9%,
var(--color0) 50%,
var(--color6) 50%,
var(--color7) 62.4%,
var(--color1) 62.4%,
var(--color1) 62.5%
);
transform-origin: 50% 50%;
transform: rotate(calc(-0.625 * 180deg));
}
.huxy-animate-border-ring {
position: relative;
width: var(--size);
height: var(--size);
margin: 0 auto;
border-radius: 50%;
.border-ring-item {
--color: rgba(0, 0, 0, 0.1);
position: absolute;
width: var(--itemWidth);
height: var(--itemHeight);
background: var(--color);
top: calc(var(--size) / 2);
left: 0;
transform-origin: calc(var(--size) / 2) calc(var(--itemHeight) / 2);
}
}
@keyframes huxy-animate-border-circle {
0% {
transform: translate(-50%, -50%) rotateZ(var(--rotateStart));
}
100% {
transform: translate(-50%, -50%) rotateZ(var(--rotateEnd));
}
}
.huxy-animate-border-circle {
position: relative;
margin: 0 auto;
width: var(--size);
height: var(--size);
border-radius: 50%;
box-shadow: 0px 0px 50px 15px var(--shadowInColor), inset 0px 0px 50px 15px var(--shadowInColor);
}
.border-circle-item {
box-sizing: border-box;
border-radius: 50%;
position: absolute;
width: var(--itemSize);
height: var(--itemSize);
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotateZ(var(--rotateStart));
border: var(--borderWidth) solid transparent;
animation: huxy-animate-border-circle var(--time) linear infinite;
}
css 可绘制各种样式背景与边框,主要使用 linear-gradien 、border 、clip-path 等实现。

.bg-box-grid {
--bgColor: hsla(0, 0%, 100%, 1);
--cubeColor: hsla(0, 0%, 100%, 0.3);
--size: 20% 20%;
background-image: linear-gradient(var(--bgColor) 15px, transparent 0), linear-gradient(90deg, var(--bgColor) 15px, transparent 0);
background-size: var(--size);
background-color: var(--cubeColor);
}
.bg-box-dot {
background-image: linear-gradient(var(--bgColor) 14px, transparent 0), linear-gradient(90deg, var(--cubeColor) 1px, transparent 0);
background-size: 15px 15px, 15px 15px;
}
.bg-box-img {
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
height:100%;
width:100%;
overflow: hidden;
}
.corner {
position: relative;
background: var(--bgColor);
border: 2px solid var(--borderColor);
}
.corner:before,
.corner:after {
content: "";
display: block;
position: absolute;
background-color: transparent;
}
.corner:before {
top: -2px;
bottom: -2px;
left: var(--cornerSize);
right: var(--cornerSize);
border-top: 2px solid var(--bgColor);
border-bottom: 2px solid var(--bgColor);
}
.corner:after {
left: -2px;
right: -2px;
top: var(--cornerSize);
bottom: var(--cornerSize);
border-left: 2px solid var(--bgColor);
border-right: 2px solid var(--bgColor);
}
.corner-half {
position: relative;
border: 2px solid transparent;
}
.corner-half:before,
.corner-half:after {
content: "";
display: block;
position: absolute;
width: calc(var(--cornerSize) + 2px);
height: calc(var(--cornerSize) + 2px);
background-color: transparent;
z-index: 3;
}
.corner-half:before {
left: -2px;
bottom: -2px;
border-left: 2px solid var(--borderColor);
border-bottom: 2px solid var(--borderColor);
}
.corner-half:after {
top: -2px;
right: -2px;
border-top: 2px solid var(--borderColor);
border-right: 2px solid var(--borderColor);
}
.huxy-title-border-wrap {
font-size: var(--fontSize);
position: relative;
padding-top: 0.713em;
.huxy-title-border-top {
position: absolute;
top: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
z-index: 3;
&:before,
&:after {
content: "";
height: var(--borderSize);
background: var(--borderColor);
flex-grow: 1;
}
&:before {
margin-right: var(--space);
}
&:after {
margin-left: var(--space);
}
}
}
@keyframes huxy-border-animate-dash {
100% {
background-position: 6% 0, -6% 100%, 0 -6%, 100% 6%;
}
}
.huxy-border-animate-dash {
position: relative;
padding: var(--space);
box-sizing: border-box;
border: 1px solid transparent;
background: linear-gradient(90deg, var(--borderColor) 50%, transparent 0) repeat-x, linear-gradient(90deg, var(--borderColor) 50%, transparent 0) repeat-x, linear-gradient(0deg, var(--borderColor) 50%, transparent 0) repeat-y, linear-gradient(0deg, var(--borderColor) 50%, transparent 0) repeat-y;
background-size: 8px 2px, 8px 2px, 2px 8px, 2px 8px;
background-position: 0 0, 0 100%, 0 0, 100% 0;
animation: huxy-border-animate-dash 0.5s infinite linear;
}
@keyframes huxy-border-animate-solid {
0%, 100% {
clip-path: inset(0% 0% 66% 66%);
}
8.33% {
clip-path: inset(0% 0% 33% 99%);
}
16.66% {
clip-path: inset(33% 0% 0% 99%);
}
25% {
clip-path: inset(66% 0% 0% 66%);
}
33.33% {
clip-path: inset(99% 0% 0% 33%);
}
41.66% {
clip-path: inset(99% 33% 0% 0%);
}
50% {
clip-path: inset(66% 66% 0% 0%);
}
58.33% {
clip-path: inset(33% 99% 0% 0%);
}
66.66% {
clip-path: inset(0% 99% 33% 0%);
}
75% {
clip-path: inset(0% 66% 66% 0%);
}
83.33% {
clip-path: inset(0% 33% 99% 0%);
}
91.66% {
clip-path: inset(0% 0% 99% 33%);
}
}
.huxy-border-animate-solid::before, .huxy-border-animate-solid::after {
content: "";
position: absolute;
--size: calc(0px - var(--space));
top: var(--size);
left: var(--size);
right: var(--size);
bottom: var(--size);
border: 2px solid var(--borderColor);
border-radius: var(--space);
animation: huxy-border-animate-solid 6s infinite linear;
}
css 可绘制各种效果文本,主要使用 text-shadow 、filter 、background-clip 等属性实现。

.text-3d-container {
position: relative;
width: var(--width);
height: var(--width);
transform-style: preserve-3d;
perspective: 1000px;
perspective-origin: 50% 50%;
.text-3d-item {
position: absolute;
width: calc(var(--width) - var(--padding) * 2);
height: calc(var(--width) - var(--padding) * 2);
left: var(--padding);
top: var(--padding);
background-color: transparent;
transform-style: preserve-3d;
backface-visibility: hidden;
transform: rotateX(0deg) rotateY(90deg) rotateZ(var(--current));
animation: huxy-text-3d var(--time) linear var(--delay) infinite;
.text-3d-item-card {
transform-style: preserve-3d;
transform-origin: 50% 50%;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
transform: rotateX(0deg) rotateY(90deg) rotateZ(var(--rotate));
}
}
}

@keyframes huxy-text-filter {
0% {
letter-spacing: -2.2rem;
filter: blur(0.3rem);
}
50% {
filter: blur(0.5rem);
}
90%, 100% {
letter-spacing: 0.5rem;
filter: blur(0rem);
}
}
.huxy-text-filter {
height: 32px;
position: relative;
filter: contrast(20);
overflow: hidden;
}
.text-filter {
text-transform: uppercase;
display: block;
position: absolute;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
letter-spacing: -2.2rem;
animation: huxy-text-filter var(--time) infinite alternate ease-in-out;
}
@keyframes huxy-text-loading {
0% {
max-width: 0;
}
}
.huxy-text-loading {
position: relative;
color: var(--backColor);
display: inline-block;
&::before {
content: attr(data-text);
position: absolute;
overflow: hidden;
max-width: 100%;
white-space: nowrap;
color: var(--frontColor);
animation: huxy-text-loading var(--time) linear infinite;
}
}
@keyframes huxy-text-masked {
0% {
background-position: 0 0;
}
100% {
background-position: -100% 0;
}
}
.huxy-text-masked {
background-image: linear-gradient(to right, var(--startColor), var(--endColor) 50%, var(--startColor) 50%, var(--endColor) 100%, var(--startColor));
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 200% 100%;
animation: huxy-text-masked 2s infinite linear;
}

[tooltip] {
position: relative;
&::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: calc(100% + 10px);
left: 50%;
transform: translate(-50%, 0);
padding: 5px;
border-radius: 4px;
color: #fff;
background-color: #313131;
white-space: nowrap;
z-index: 1;
}
&:hover {
&::after {
display: block;
}
}
}
很多时候,不必使用 js ,css 就可以实现很多功能和效果。以上是从之前实践中简单抽取了一些样式,封装为 css materials ,便于使用。使用方式如下:
import {IconAnimate, Css3dModel, Fliper, IconCarousel} from '@huxy/materials';
const Index = props => {
return (
<div>
<div style={box}>
<IconAnimate objs={animateObjs} models={cssModels} />
</div>
<div style={box}>
<Fliper
front={<div style={{background: 'var(--blue1)', ...fliperStyle}}>正面</div>}
back={<div style={{background: 'var(--orange1)', ...fliperStyle}}>反面</div>}
style={{height: 200, width: 200, margin: '0 auto'}}
/>
</div>
<div style={box}>
<IconCarousel list={models} />
</div>
</div>
);
};
export default Index;
以上效果可在 ihuxy 上查看。
FAQs
js css materials
We found that @huxy/materials demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.

Research
/Security News
A large-scale campaign abused GitHub Actions in compromised repositories to exploit CVE-2026-41940 in cPanel and WHM and steal server credentials.

Security News
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.