49% OFF - DIY Quilted Christmas Tree Kit

$29.99  - $129.99
/** * 优惠码组件模型类 * 处理优惠码的显示和交互逻辑 */ class SpzCustomDiscountCodeModel extends SPZ.BaseElement { constructor(element) { super(element); // 复制按钮和内容的类名 this.copyBtnClass = "discount_code_btn" this.copyClass = "discount_code_value" } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { // 初始化服务 this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); } /** * 渲染优惠码组件 * @param {Object} data - 渲染数据 */ doRender_(data) { return this.templates_ .findAndRenderTemplate(this.element, Object.assign(this.getDefaultData(), data) ) .then((el) => { this.clearDom(); this.element.appendChild(el); // 绑定复制代码功能 this.copyCode(el, data); }); } /** * 获取渲染模板 * @param {Object} data - 渲染数据 */ getRenderTemplate(data) { const renderData = Object.assign(this.getDefaultData(), data); return this.templates_ .findAndRenderTemplate(this.element, renderData) .then((el) => { this.clearDom(); return el; }); } /** * 清除DOM内容 */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * 获取默认数据 * @returns {Object} 默认数据对象 */ getDefaultData() { return { isMobile: appDiscountUtils.judgeMobile(), isRTL: appDiscountUtils.judgeRTL(), image_domain: this.win.SHOPLAZZA.image_domain, copyBtnClass: this.copyBtnClass, copyClass: this.copyClass } } /** * 复制优惠码功能 * @param {Element} el - 当前元素 */ copyCode(el) { const copyBtnList = el.querySelectorAll(`.${this.copyBtnClass}`); if (copyBtnList.length > 0) { copyBtnList.forEach(item => { item.onclick = async () => { // 确保获取正确的元素和内容 const codeElement = item.querySelector(`.${this.copyClass}`); if (!codeElement) return; // 获取纯文本内容 const textToCopy = codeElement.innerText.trim(); // 尝试使用现代API,如果失败则使用备用方案 try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(textToCopy); } else { throw new Error('Clipboard API not available'); } // 显示复制成功提示 this.showCopySuccessToast(textToCopy, el); } catch (err) { console.error('Modern clipboard API failed, trying fallback...', err); // 使用备用复制方案 this.fallbackCopy(textToCopy, el); } const discountId = item.dataset["discountId"]; // 跳转决策: is_redirection + link(可选覆盖) const setting = { is_redirection: item.dataset["redirection"] === "true", link: item.dataset["link"], }; const landingUrl = `/promotions/discount-default/${discountId}`; const finalUrl = appDiscountUtils.resolveDiscountHref(setting, landingUrl); if (finalUrl && appDiscountUtils.inProductBody(this.element)) { this.win.open(finalUrl, '_blank', 'noopener'); } } }) } } /** * 使用 execCommand 的复制方案 * @param {string} codeText - 要复制的文本 * @param {Element} el - 当前元素 */ fallbackCopy(codeText, el) { const textarea = this.win.document.createElement('textarea'); textarea.value = codeText; // 设置样式使文本框不可见 textarea.style.position = 'fixed'; textarea.style.left = '-9999px'; textarea.style.top = '0'; // 添加 readonly 属性防止移动端虚拟键盘弹出 textarea.setAttribute('readonly', 'readonly'); this.win.document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { this.win.document.execCommand('copy'); // 显示复制成功提示 this.showCopySuccessToast(codeText, el); } catch (err) { console.error('Copy failed:', err); } this.win.document.body.removeChild(textarea); } /** * 创建 Toast 元素 * @returns {Element} 创建的 Toast 元素 */ createToastEl_() { const toast = document.createElement('ljs-toast'); toast.setAttribute('layout', 'nodisplay'); toast.setAttribute('hidden', ''); toast.setAttribute('id', 'discount-code-toast'); toast.style.zIndex = '1051'; return toast; } /** * 挂载 Toast 元素到 body * @returns {Element} 挂载的 Toast 元素 */ mountToastToBody_() { const existingToast = this.win.document.getElementById('discount-code-toast'); if (existingToast) { return existingToast; } const toast = this.createToastEl_(); this.win.document.body.appendChild(toast); return toast; } /** * 复制成功的提醒 * @param {string} codeText - 要复制的文本 * @param {Element} el - 当前元素 */ showCopySuccessToast(codeText, el) { const $toast = this.mountToastToBody_(); SPZ.whenApiDefined($toast).then(toast => { toast.showToast("Discount code copied !"); this.codeCopyInSessionStorage(codeText); }); } /** * 复制优惠码成功后要存一份到本地存储中,购物车使用 * @param {string} codeText - 要复制的文本 */ codeCopyInSessionStorage(codeText) { try { sessionStorage.setItem('other-copied-coupon', codeText); } catch (error) { console.error(error) } } } // 注册自定义元素 SPZ.defineElement('spz-custom-discount-code-model', SpzCustomDiscountCodeModel);
/** * Custom discount code component that handles displaying and managing discount codes * @extends {SPZ.BaseElement} */ class SpzCustomDiscountCode extends SPZ.BaseElement { constructor(element) { super(element); // API endpoint for fetching discount codes this.getDiscountCodeApi = "\/api\/storefront\/promotion\/code\/list"; // Debounce timer for resize events this.timer = null; // Current variant ID this.variantId = "39e6fa0b-46a1-4a85-a6a8-ca0b19a8ec43"; // Store discount code data this.discountCodeData = {} } /** * Check if layout is supported * @param {string} layout - Layout type * @return {boolean} */ isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } /** * Initialize component after build */ buildCallback() { this.templates_ = SPZServices.templatesForDoc(); this.viewport_ = this.getViewport(); // Bind methods to maintain context this.render = this.render.bind(this); this.resize = this.resize.bind(this); this.switchVariant = this.switchVariant.bind(this); } /** * Setup component when mounted */ mountCallback() { this.getData(); // Add event listeners this.viewport_.onResize(this.resize); this.win.document.addEventListener('dj.variantChange', this.switchVariant); } /** * Cleanup when component is unmounted */ unmountCallback() { this.viewport_.removeResize(this.resize); this.win.document.removeEventListener('dj.variantChange', this.switchVariant); // 清除定时器 if (this.timer) { clearTimeout(this.timer); this.timer = null; } } /** * Handle resize events with debouncing */ resize() { if (this.timer) { clearTimeout(this.timer) this.timer = null; } this.timer = setTimeout(() => { if (appDiscountUtils.inProductBody(this.element)) { this.render(); } else { this.renderSkeleton(); } }, 200); } /** * Handle variant changes * @param {Event} event - Variant change event */ switchVariant(event) { const variant = event.detail.selected; if (variant.product_id == '1984daf3-5c8e-4cf6-a4cb-f1c312e39526' && variant.id != this.variantId) { this.variantId = variant.id; this.getData(); } } /** * Fetch discount code data from API */ getData() { if (appDiscountUtils.inProductBody(this.element)) { const reqBody = { product_id: "1984daf3-5c8e-4cf6-a4cb-f1c312e39526", variant_id: this.variantId, product_type: "default", } if (!reqBody.product_id || !reqBody.variant_id) return; this.discountCodeData = {}; this.win.fetch(this.getDiscountCodeApi, { method: "POST", body: JSON.stringify(reqBody), headers: { "Content-Type": "application/json" } }).then(async (response) => { if (response.ok) { let data = await response.json(); if (data.list && data.list.length > 0) { data.list[0].product_setting.template_config = JSON.parse(data.list[0].product_setting.template_config); // Format timestamps to local timezone const zone = this.win.SHOPLAZZA.shop.time_zone; data.list = data.list.map(item => { if(+item.ends_at !== -1) { item.ends_at = appDiscountUtils.convertTimestampToFormat(+item.ends_at, zone); } item.starts_at = appDiscountUtils.convertTimestampToFormat(+item.starts_at, zone); return item; }); } this.discountCodeData = data; this.render(); } else { this.clearDom(); } }).catch(err => { console.error("discount_code", err) this.clearDom(); }); } else { this.renderSkeleton(); } } /** * Clear component DOM except template */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * Render discount codes with formatted dates */ render() { // Render using discount code model SPZ.whenApiDefined(document.querySelector('#spz_custom_discount_code_model')).then(renderApi => { renderApi.doRender_({ discountCodeData: this.discountCodeData }) }).catch(err => { this.clearDom(); }) } renderSkeleton() { // Render template for non-product pages this.templates_ .findAndRenderTemplate(this.element, { isMobile: appDiscountUtils.judgeMobile() }) .then((el) => { this.clearDom(); this.element.appendChild(el); }) .catch(err => { this.clearDom(); }); } } // Register custom element SPZ.defineElement('spz-custom-discount-code', SpzCustomDiscountCode);
Style:  🦌 Classic Reindeer
Quantity
$59.99 全球免运费
Free returns
Sustainably made
Secure payments

Description

🎄 A COZY CHRISTMAS, MADE BY HAND
A relaxing holiday project with a beautiful finish

Create a Christmas Memory With Your Own Hands

Layer festive fabrics into a soft, dimensional Christmas tree that feels warm, nostalgic and uniquely yours. Enjoy the making, then display the finished piece as part of your holiday tradition.

🧵 Hands-On Holiday Craft 🎄 Layered Fabric Design 🏡 Display After Making 🎁 Thoughtful Craft Gift
More meaning than mass-produced decor

Some Decorations Fill a Space. Handmade Ones Fill It With Memories.

Holiday shelves can look festive and still feel impersonal. This project gives you time to slow down, work with your hands and create something that carries your own care into the season.

Why this project feels special

Quiet Craft Time That Becomes Holiday Decor

Every layer adds texture, color and personality. The process is part of the experience, and the finished tree is something you can enjoy long after the last piece is placed.

🧘

A Calmer Holiday Moment

Step away from screens and seasonal rushing for a focused hands-on activity.

🧵

Rich Layered Texture

Overlapping fabric sections create a dimensional finish that flat printed decor cannot match.

🏡

Made to Be Displayed

Style the finished tree on a mantel, entry table, shelf, desk or holiday centerpiece.

❤️

A Personal Keepsake

Your time and creativity make the completed decoration feel more meaningful.

Build it layer by layer

Watch a Flat Collection of Fabrics Take Shape

The pointed fabric sections overlap to form a full tabletop tree. Coordinated holiday prints give every angle a warm patchwork look, while the character topper completes the design.

  • Coordinated festive fabric patterns
  • Soft, dimensional layered construction
  • Charming character-led designs
  • A finished piece with handmade personality
Returns to the product selection area at the top of this page.
A simple creative rhythm

Make It One Satisfying Step at a Time

Follow the instructions supplied with your selected kit and enjoy seeing the tree grow fuller with every completed layer.

1

Prepare

Organize the coordinated pieces and review the guide for your chosen design.

2

Build the Layers

Shape and arrange each fabric section to create the tree's dimensional form.

3

Finish & Display

Add the final details, place your topper and find the perfect holiday spot.

A design for every holiday personality

Choose the Character That Makes You Smile

Classic red, evergreen, cream and nostalgic prints bring the collection together, while each topper gives the finished tree its own personality.

Please note: Design details and package contents may vary by selected style. Review the product options shown in the purchase area before ordering.
The charm is in the details

Soft Texture, Nostalgic Prints and a Full Handmade Look

Checks, snowflakes, holiday characters and botanical motifs work together across the layered form. Up close, the stitching and fabric texture give the piece the cozy character people love about handmade decor.

  • Classic Christmas color palette
  • Mixed prints for a collected patchwork feel
  • Dimensional silhouette from every angle
Make it part of your home

Small Enough to Style. Special Enough to Notice.

Add handmade warmth to a mantel, dining-room sideboard, entryway, bedroom shelf, craft room or seasonal table display.

An experience and a finished keepsake

A Thoughtful Gift for Someone Who Loves to Create

Give the kit as a relaxing holiday project, make it together during a cozy afternoon, or complete the tree yourself and present it as a decoration made with care.

🎁

For Craft Lovers

A seasonal project with a beautiful result they can proudly display.

👩👧

For Shared Time

A hands-on activity for a quiet afternoon with family or a friend.

🏠

For Cozy Homes

A soft alternative to shiny, mass-produced seasonal decorations.

❤️

For Meaningful Giving

A gift that carries creativity, time and a personal holiday story.

Why a DIY tree feels different

More Than Another Decoration

ExperienceDIY Patchwork TreeOrdinary Store Decor
Hands-on creative time✓ Part of the experienceUsually none
Soft dimensional texture✓ Layered fabric finishVaries
Personal story✓ Made by youMass-produced
Gift experience✓ Make, share and displayDisplay only
Questions before you begin

Frequently Asked Questions

Is this a finished decoration?

No. It is a DIY craft project intended to be assembled into the finished tree shown. Please review the selected product option for exact details.

Is it suitable for beginners?

The project is presented as an approachable holiday craft. Work at your own pace and follow the instructions supplied with your selected kit.

Where can I display the completed tree?

It works beautifully on mantels, shelves, sideboards, desks, entry tables and seasonal centerpiece displays.

What is included?

Package contents may differ by style. Check the currently selected variant and package information in the purchase area for the most accurate list.

Can it be given as a gift?

Yes. It can be given as a creative project or completed first and presented as a personal handmade decoration.

Will colors look exactly the same as my screen?

Colors may appear slightly different depending on lighting and screen settings. Small variations are normal for fabric-based craft products.

Make this Christmas feel handmade

Create Something You Will Be Proud to Bring Out Each Holiday

Choose your favorite design, enjoy the quiet rhythm of making it, and welcome a little more warmth into your Christmas home.

Secure checkout options are displayed in the product purchase area.