{"id":152,"date":"2026-10-15T06:53:02","date_gmt":"2026-10-15T06:53:02","guid":{"rendered":"https:\/\/webdevpuneet.com\/blog\/?p=152"},"modified":"2026-10-15T06:54:58","modified_gmt":"2026-10-15T06:54:58","slug":"how-to-animate-css-properties-with-gsap-with-live-examples","status":"publish","type":"post","link":"https:\/\/webdevpuneet.com\/blog\/how-to-animate-css-properties-with-gsap-with-live-examples\/","title":{"rendered":"How to Animate CSS Properties with GSAP (With Live Examples)"},"content":{"rendered":"<p><!--\n================================================================\n  POST TITLE (paste into the WordPress title field \u2014 it becomes the page H1)\n  How to Animate CSS Properties with GSAP (With Live Examples)\n\n  SEO TITLE (54 chars, target 50\u201360, max 60 \u2014 \"Optimize SEO\" panel \u2192 SEO TITLE)\n  How to Animate CSS Properties with GSAP (8 Live Demos)\n\n  SEO DESCRIPTION (152 chars, target 150\u2013160, max 160 \u2014 \"Optimize SEO\" panel \u2192 SEO DESCRIPTION)\n  Animate CSS properties with GSAP: transforms, color, border-radius, box-shadow, filter, clip-path, CSS variables and SVG strokes, each with a live demo.\n\n  HOW TO PASTE: click the first line after this comment, press Ctrl+Shift+End, Ctrl+C.\n  In WordPress click \"Type \/ to choose a block\", then Ctrl+V.\n  Fallback: \u22ee menu \u2192 Code editor \u2192 paste.\n================================================================\n--><\/p>\n\n\n<p class=\"wp-block-paragraph\">Most GSAP tutorials start with <code>x<\/code> and <code>y<\/code> and stop there, which leaves out a real question: which CSS properties can GSAP actually animate, and does it treat them all the same way? The honest answer is no \u2014 GSAP handles a plain numeric property like <code>opacity<\/code> very differently under the hood from a compound value like <code>box-shadow<\/code>, a shape like <code>clip-path<\/code>, or a browser-registered custom property like <code>--pct<\/code>. This post walks through eight real CSS properties one at a time \u2014 transforms, color, border-radius, box-shadow, filter, clip-path, a CSS variable, and an SVG stroke \u2014 showing exactly what GSAP does with each one and why it works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo below is <strong>live and interactive<\/strong> right here in the article \u2014 click the buttons and watch the property change. Each embed also has a <strong>Fork &amp; Edit<\/strong> button that opens the exact code, already running, in <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\" target=\"_blank\" rel=\"noopener\">My Code<\/a> \u2014 webdevpuneet&#8217;s free in-browser editor. Swap a value, hit run again, and watch what changes \u2014 that&#8217;s the fastest way to build real intuition for how GSAP touches CSS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">New to GSAP itself, before its relationship with specific CSS properties? &#8220;GSAP Tutorial for Beginners&#8221; on this blog covers <code>gsap.to()<\/code>, timelines, stagger, and ScrollTrigger from the ground up, and the free, click-based <a href=\"https:\/\/webdevpuneet.com\/gsap-playground\/\" target=\"_blank\" rel=\"noopener\">GSAP Playground<\/a> builds on exactly that foundation across 55 lessons.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why GSAP instead of a CSS transition for these?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A plain CSS <code>transition<\/code> genuinely animates most of what&#8217;s below too \u2014 this isn&#8217;t an argument that GSAP is required. What GSAP adds is control: starting, pausing, reversing, or replaying any of these tweens from JavaScript on demand, sequencing several of them precisely with a timeline, and \u2014 for a handful of properties below \u2014 interpolation a plain transition can&#8217;t do at all, like tweening a shape&#8217;s individual polygon points or drawing an SVG path in stroke by stroke. Every demo here is deliberately small, but each one demonstrates a real category of CSS value GSAP has to actually understand to animate correctly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Transform properties \u2014 x, y, scale, rotation, skew<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The most common case first, because it&#8217;s the one every other property gets compared against: several transform values combined into one tween.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/1.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP Transform Properties Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.box', {\n  x: 140,\n  y: -20,\n  scale: 1.3,\n  rotation: 25,\n  skewX: 8,\n  duration: 1,\n  ease: 'power2.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In plain CSS, <code>x<\/code>, <code>y<\/code>, <code>scale<\/code>, <code>rotation<\/code>, and <code>skewX<\/code>\/<code>skewY<\/code> would all have to be packed into one <code>transform<\/code> string by hand, and animating them at different speeds relative to each other would mean juggling that string yourself. GSAP treats each one as its own independent numeric property, combines them into the correct <code>matrix()<\/code> behind the scenes every frame, and lets each one carry its own value without any string concatenation from you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> anything involving movement, sizing, or rotation \u2014 far and away the most common category of UI animation. <strong>Tip:<\/strong> always prefer these transform shorthands over animating <code>top<\/code>\/<code>left<\/code>\/<code>width<\/code>\/<code>height<\/code> directly \u2014 transforms run on the compositor thread and don&#8217;t trigger layout recalculation the way box-model properties do.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Color properties \u2014 backgroundColor, color, borderColor<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GSAP&#8217;s core understands color values well enough to interpolate between two completely different-looking colors smoothly, not just fade one out and the other in.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/2.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP Color Properties Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.card', {\n  backgroundColor: '#e8f9ee',\n  color: '#15803d',\n  borderColor: '#16a34a',\n  duration: 0.6,\n  ease: 'power1.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GSAP parses hex, <code>rgb()<\/code>\/<code>rgba()<\/code>, <code>hsl()<\/code>, and named CSS colors, converts both ends to the same color space, and tweens each channel independently \u2014 which is exactly what makes a status card&#8217;s background, text, and border all shift together read as one coherent color change instead of three separate ones landing slightly out of sync.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> status changes, confirmations, and any UI feedback that reads as &#8220;this state changed&#8221; through color. <strong>Tip:<\/strong> keep the color pairs reasonably close in lightness when tweening several color properties together (as this demo does) \u2014 wildly different luminance values between start and end can make the mid-tween color look muddy for a frame or two.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. border-radius \u2014 morphing a shape<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each of the four corners is its own value, which means each one can be tweened to a different target \u2014 enough to morph a clean square into an organic blob shape.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/3.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP border-radius Morph Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.blob', {\n  borderTopLeftRadius: '60% 40%',\n  borderTopRightRadius: '40% 60%',\n  borderBottomLeftRadius: '40% 60%',\n  borderBottomRightRadius: '60% 40%',\n  rotation: 8,\n  duration: 1.1,\n  ease: 'power2.inOut'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The shorthand <code>border-radius<\/code> property itself can&#8217;t be tweened as one value when the four corners are heading toward different targets, so this splits it into its four longhand properties \u2014 <code>borderTopLeftRadius<\/code> and so on \u2014 each animated independently. Each one also accepts the two-value elliptical syntax (<code>'60% 40%'<\/code> for horizontal\/vertical radius), which is what turns a simple rounded square into something that reads as organic rather than mechanically rounded.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> decorative blob shapes, playful loading states, or a card that subtly softens on hover. <strong>Tip:<\/strong> combine a small <code>rotation<\/code> alongside the corner tweens, as this demo does \u2014 a pure corner morph with zero rotation can read as strangely static even while it&#8217;s actively animating.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. box-shadow \u2014 a compound value<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Unlike the transform shorthands above, <code>box-shadow<\/code> genuinely is one single value in CSS \u2014 offset, blur, spread, and color all in one string. GSAP still animates it correctly.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/4.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP box-shadow Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.card', {\n  y: -10,\n  boxShadow: '0 22px 34px rgba(99,102,241,0.28)',\n  duration: 0.5,\n  ease: 'power2.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GSAP&#8217;s CSSPlugin parses the whole <code>box-shadow<\/code> string on both ends of the tween, matches up the corresponding numeric pieces (offset-x, offset-y, blur radius, spread radius, and each color channel), and interpolates each one in place \u2014 something a naive string-splice couldn&#8217;t do reliably, and that a plain CSS transition on <code>box-shadow<\/code> genuinely does too, just without the pause\/reverse\/sequence control GSAP adds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> a card or button &#8220;lifting&#8221; on hover or focus \u2014 pairing a small upward <code>y<\/code> move with a larger, softer shadow is what sells the depth. <strong>Tip:<\/strong> keep the resting-state shadow present (as this demo&#8217;s idle <code>0 1px 2px<\/code> shadow is) rather than starting from <code>none<\/code> \u2014 animating from zero blur\/spread to a large one can look like the shadow is &#8220;growing into existence&#8221; rather than lifting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. filter \u2014 blur, brightness, grayscale together<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Like <code>box-shadow<\/code>, <code>filter<\/code> is a compound value \u2014 potentially several filter functions chained in one string \u2014 and GSAP tweens the whole thing as one property.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/5.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP filter Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.tile', {\n  filter: 'blur(0px) brightness(1) grayscale(0)',\n  duration: 0.9,\n  ease: 'power1.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Both the starting and ending <code>filter<\/code> strings need the same functions listed, in the same order, for GSAP to match each one up and interpolate its number \u2014 <code>blur(6px)<\/code> pairs with <code>blur(0px)<\/code>, <code>brightness(0.75)<\/code> with <code>brightness(1)<\/code>, and so on. That&#8217;s exactly the &#8220;focus in&#8221; effect used for lazy-loaded images and content reveals across the web: start blurred, desaturated, and dim; land sharp, colored, and at full brightness.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> image reveals, &#8220;focus in&#8221; content loading effects, and disabling\/enabling states (grayscale + reduced brightness reads clearly as &#8220;inactive&#8221;). <strong>Tip:<\/strong> <code>filter<\/code> is genuinely expensive to render compared to transforms \u2014 fine for a handful of elements animating at once, but worth avoiding on large lists or during a scroll-linked animation with dozens of elements.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. clip-path \u2014 a shape-based wipe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Where <code>filter<\/code> and <code>box-shadow<\/code> are still fundamentally numbers under the hood, <code>clip-path<\/code> pushes further \u2014 GSAP tweens the individual coordinate points of a polygon.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/6.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP clip-path Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.panel', {\n  clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',\n  duration: 0.9,\n  ease: 'power2.inOut'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This only works because both the starting polygon (<code>0 0, 0 0, 0 100%, 0 100%<\/code> \u2014 a collapsed sliver on the left edge) and the ending one list exactly four points each. GSAP walks the two point lists in order and tweens each matching x\/y pair independently, which is what produces the wipe \u2014 the whole rectangle appears to slide open from left to right, entirely through clipping, with no change to the element&#8217;s actual size or position.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> reveal wipes, image transitions, and any effect where content should appear to be &#8220;uncovered&#8221; rather than faded or scaled in. <strong>Tip:<\/strong> keep the point count identical between the start and end <code>clip-path<\/code> values \u2014 a mismatched number of points is the single most common reason a clip-path tween snaps instead of animating smoothly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. A CSS custom property \u2014 driving a progress meter<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GSAP can tween a CSS custom property directly, and registering it with <code>@property<\/code> as a number makes it a first-class, typed value the rest of your CSS can rely on.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/7.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP CSS Custom Property Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@property --pct {\n  syntax: '&lt;number&gt;';\n  inherits: false;\n  initial-value: 0;\n}\n\ngsap.to('.meterFill', {\n  '--pct': 82,\n  duration: 1.2,\n  ease: 'power1.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GSAP interpolates the number itself and writes it back with <code>style.setProperty<\/code> every frame, so the tween works even on an unregistered variable. What <code>@property<\/code> adds is on the browser&#8217;s side: without it, a custom property is an untyped string, so a plain CSS <code>transition<\/code> or <code>@keyframes<\/code> on it can only snap, and <code>inherits: false<\/code> stops every descendant recalculating it each frame. Once <code>--pct<\/code> is registered as a <code>&lt;number&gt;<\/code>, the meter&#8217;s <code>width: calc(var(--pct) * 1%)<\/code> and the percentage label \u2014 read back each frame with <code>gsap.getProperty(el, '--pct')<\/code> in this demo&#8217;s <code>onUpdate<\/code> \u2014 both stay perfectly in sync with a single source of truth.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> progress meters, gradients driven by a single number, or any case where one animated value needs to feed multiple CSS declarations at once without duplicating the tween. <strong>Tip:<\/strong> keep the <code>@property<\/code> rule even though GSAP doesn&#8217;t strictly need it \u2014 it&#8217;s what lets the same variable also animate with a CSS transition, gives it a sane initial value of 0, and documents in the stylesheet that <code>--pct<\/code> is a number.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. SVG stroke-dashoffset \u2014 drawing a path<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Not a CSS box-model property, but a close relative worth knowing: an SVG presentation attribute GSAP animates through the exact same tween syntax as everything above.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/12\/8.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP SVG stroke-dashoffset Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const length = path.getTotalLength();\ngsap.set(path, { strokeDasharray: length, strokeDashoffset: length });\n\ngsap.to(path, {\n  strokeDashoffset: 0,\n  duration: 0.9,\n  ease: 'power1.inOut'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>stroke-dasharray<\/code> set to the path&#8217;s exact length (from the native <code>getTotalLength()<\/code> method, not a guess) turns the entire stroke into one long dash with no visible gap. <code>stroke-dashoffset<\/code>, also set to that same length, then shifts that single dash completely out of view. Tweening <code>stroke-dashoffset<\/code> down to <code>0<\/code> slides the dash back into place, which reads as the line drawing itself in \u2014 a checkmark, an underline, a signature, or an icon outline.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> checkmarks, signature or underline reveals, and icon outlines that should feel hand-drawn rather than faded in. <strong>Tip:<\/strong> <code>gsap.set()<\/code> (used here to establish the starting dash state instantly, with no animation) is the same engine as <code>gsap.to()<\/code> but jumps straight to the given values \u2014 reach for it whenever a starting state needs to be set in code rather than baked into the CSS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Animating <code>top<\/code>\/<code>left<\/code>\/<code>width<\/code>\/<code>height<\/code> instead of transforms.<\/strong> Those trigger layout recalculation on every frame; <code>x<\/code>\/<code>y<\/code>\/<code>scale<\/code> (demo #1) run on the compositor and stay smooth even on weaker devices.<\/li>\n\n\n\n<li><strong>Mismatched point counts in a <code>clip-path<\/code> tween.<\/strong> Demo #6 only animates smoothly because both ends list exactly four polygon points \u2014 a different count on either side makes the shape snap instead of morph.<\/li>\n\n\n\n<li><strong>Expecting a CSS transition on a custom property to animate without <code>@property<\/code>.<\/strong> GSAP interpolates the number itself, but a plain CSS <code>transition<\/code> or <code>@keyframes<\/code> on an unregistered variable just snaps \u2014 register it as a <code>&lt;number&gt;<\/code> (as demo #7 does) if any CSS-side animation touches it.<\/li>\n\n\n\n<li><strong>Guessing an SVG path&#8217;s length instead of measuring it.<\/strong> Demo #8&#8217;s draw-in depends on <code>getTotalLength()<\/code> being exact \u2014 a hand-typed dasharray that&#8217;s too short or long leaves a visible gap or overlap at the &#8220;finished&#8221; state.<\/li>\n\n\n\n<li><strong>Reaching for <code>filter<\/code> on a large list of elements.<\/strong> It&#8217;s the most render-expensive property in this whole list; fine for a handful of elements (demo #5), worth reconsidering for dozens animating at once.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can GSAP animate any CSS property?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost any property with a numeric or interpolatable value, yes \u2014 including ones a plain CSS transition also handles, like <code>opacity<\/code>, colors, and transforms. Where GSAP goes further is compound values it parses itself (<code>box-shadow<\/code>, <code>filter<\/code>), shape data (<code>clip-path<\/code>), and CSS custom properties, which it interpolates itself (a CSS transition would need them registered with <code>@property<\/code> first). A handful of properties, like <code>display<\/code>, are inherently non-numeric and can only be toggled, not tweened.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need a GSAP plugin to animate CSS properties?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No \u2014 everything in this post uses only core GSAP (CSSPlugin, which ships built in). No plugin registration is needed for any of the eight properties covered here, including the CSS custom property in demo #7.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why does my CSS custom property jump instead of animating smoothly?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If it&#8217;s a CSS <code>transition<\/code> or <code>@keyframes<\/code> doing the animating, it&#8217;s almost always a missing <code>@property<\/code> registration: without telling the browser the property holds a <code>&lt;number&gt;<\/code> (or another interpolatable type), it&#8217;s an opaque string and can only snap. With GSAP, check that the starting value is a plain number GSAP can read \u2014 a value like <code>calc(...)<\/code> or an empty string gives it nothing to interpolate from. Demo #7 shows the registration either way.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is it better to animate box-shadow\/filter or use a transform-based alternative?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Transforms are cheaper to render, but not every effect has a transform-based equivalent \u2014 a genuine blur or a growing shadow needs <code>filter<\/code> or <code>box-shadow<\/code> respectively. Use transforms whenever the effect allows it, and reach for these heavier properties deliberately, on a reasonable number of elements at once.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the difference between clip-path and a plain width\/height reveal?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A width or height tween changes the element&#8217;s actual box size, which reflows surrounding layout as it grows. <code>clip-path<\/code> (demo #6) keeps the element&#8217;s real size fixed the entire time and only changes what&#8217;s visible \u2014 no layout shift, because changing <code>clip-path<\/code> only repaints the element and never triggers layout.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo above ships its full HTML, CSS and JS in the <strong>HTML \/ CSS \/ JS<\/strong> tabs in its own top bar. A fast way to make this stick: click <strong>Fork &amp; Edit<\/strong> on demo #6 and change the four-point polygon to a diagonal wipe instead of a left-to-right one, or fork demo #7 and swap <code>--pct<\/code>&#8216;s target from 82 to something driven by a real value in your own project. Save whichever version you like to <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\" target=\"_blank\" rel=\"noopener\">My Code<\/a> and it&#8217;s yours to keep, tweak further, or drop straight into a real project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommended next step:<\/strong> the <a href=\"https:\/\/webdevpuneet.com\/gsap-playground\/\" target=\"_blank\" rel=\"noopener\">GSAP Playground<\/a> covers tweens, timelines, stagger, and ScrollTrigger in the same live-editor format as these demos, free and click-based across 55 lessons. For more tutorials like this one, browse the <a href=\"https:\/\/webdevpuneet.com\/blog\/category\/gsap\/\" target=\"_blank\" rel=\"noopener\">GSAP<\/a> category on the blog.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most GSAP tutorials start with x and y and stop there, which leaves out a real question: which CSS properties can GSAP actually animate, and does it treat them all the same way? The honest answer is no \u2014 GSAP handles a plain numeric property like opacity very differently under the hood from a compound [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":153,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","advanced_seo_description":"Animate CSS properties with GSAP: transforms, color, border-radius, box-shadow, filter, clip-path, CSS variables and SVG strokes, each with a live demo.","jetpack_seo_html_title":"How to Animate CSS Properties with GSAP (8 Live Demos)","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"CSS animations are powerful, but knowing which properties you can animate smoothly with GSAP makes all the difference.\n\nI\u2019ve put together a guide covering 8 CSS properties you can animate with GSAP, each with a live example you can run and experiment with.\n\nWhat you\u2019ll learn:\n\nTransform \u2014 animate x, y, scale, rotation, and skew\nColor \u2014 smoothly animate backgroundColor, color, and borderColor\nBorder-radius \u2014 morph squares into organic shapes\nBox-shadow \u2014 create smooth hover and elevation effects\nFilter \u2014 animate blur, brightness, and grayscale\nClip-path \u2014 create wipe reveals and clipping animations\nCSS custom properties \u2014 animate CSS variables and progress bars\nSVG stroke-dashoffset \u2014 create stroke-drawing animations\n\nEvery example includes interactive controls so you can see the animations in action.\n\nRead the full tutorial and explore the live demos on WebDevPuneet.\n\n#GSAP #CSS #JavaScript #WebDevelopment #FrontendDevelopment #WebDesign #Animation #Coding","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[15,9],"tags":[],"class_list":["post-152","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gsap","category-tutorials","et-has-post-format-content","et_post_format-et-post-format-standard"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/phrh5V-2s","jetpack_featured_media_url":"https:\/\/i0.wp.com\/webdevpuneet.com\/blog\/wp-content\/uploads\/2026\/10\/gsap-css-properties-featured-1200x630-1.webp?fit=1200%2C630&ssl=1","_links":{"self":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/152","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/comments?post=152"}],"version-history":[{"count":1,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/152\/revisions"}],"predecessor-version":[{"id":154,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/152\/revisions\/154"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media\/153"}],"wp:attachment":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media?parent=152"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/categories?post=152"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/tags?post=152"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}