<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ana Elena Ulate Salas</title>
    <description>The latest articles on DEV Community by Ana Elena Ulate Salas (@aulate).</description>
    <link>https://dev.to/aulate</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3476865%2Ffc068ec8-00d7-42cc-a858-45d8c3fb1194.jpg</url>
      <title>DEV Community: Ana Elena Ulate Salas</title>
      <link>https://dev.to/aulate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aulate"/>
    <language>en</language>
    <item>
      <title>🐾 Building a Secure Comment System in Laravel (with ID Authentication and Arithmetic CAPTCHA)</title>
      <dc:creator>Ana Elena Ulate Salas</dc:creator>
      <pubDate>Mon, 13 Oct 2025 20:23:56 +0000</pubDate>
      <link>https://dev.to/aulate/building-a-secure-comment-system-in-laravel-with-id-authentication-and-arithmetic-captcha-ggb</link>
      <guid>https://dev.to/aulate/building-a-secure-comment-system-in-laravel-with-id-authentication-and-arithmetic-captcha-ggb</guid>
      <description>&lt;p&gt;In this post, I’ll share how I developed a small but complete &lt;strong&gt;comment system in Laravel 12&lt;/strong&gt;, focused on authentication, data validation, and basic security.&lt;br&gt;
The project is called &lt;strong&gt;Pet Shop – Comment System&lt;/strong&gt;, and it was a great opportunity to practice good coding practices and understand Laravel’s core features.&lt;/p&gt;


&lt;h2&gt;
  
  
  🎯 Project Goal
&lt;/h2&gt;

&lt;p&gt;The main goal was to build a functional web application that simulates a login process, comment submission, and message confirmation — applying Laravel’s validation and security features.&lt;/p&gt;

&lt;p&gt;I wanted to create something &lt;strong&gt;minimal yet complete&lt;/strong&gt;, that demonstrates the foundations of modern Laravel development.&lt;/p&gt;


&lt;h2&gt;
  
  
  ⚙️ Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Laravel 12 (PHP 8.2+)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL / MariaDB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bootstrap 5 (via CDN)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blade Templates&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Artisan CLI&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🔐 ID-Based Authentication
&lt;/h2&gt;

&lt;p&gt;Instead of using an email address for login, I implemented &lt;strong&gt;authentication by ID number&lt;/strong&gt;, a method often used in local systems.&lt;br&gt;
This allowed me to create custom validation rules and flexible user management.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;AuthController&lt;/code&gt;, the login logic works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'cedula'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|min:6'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cedula'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cedula&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/home'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withErrors&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'login'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid credentials'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This manual authentication flow helped me understand Laravel’s internals without relying on Breeze or Jetstream.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧮 Arithmetic CAPTCHA
&lt;/h2&gt;

&lt;p&gt;To prevent automated logins and spam, I implemented a &lt;strong&gt;simple arithmetic CAPTCHA&lt;/strong&gt; that randomly generates addition, subtraction, or multiplication problems.&lt;/p&gt;

&lt;p&gt;Here’s a simplified version of the logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$num1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$num2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$operator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;

&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$operator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'+'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$num1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$num2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$num1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$num2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$num1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$num2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'captcha_result'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user submits the form, the validation checks if the provided answer matches the stored result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;captcha&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'captcha_result'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withErrors&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'captcha'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Incorrect CAPTCHA result.'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a simple, effective solution that doesn’t require external APIs.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Comment System
&lt;/h2&gt;

&lt;p&gt;Once logged in, users can create comments about services such as &lt;strong&gt;baths, walks, or grooming&lt;/strong&gt;.&lt;br&gt;
Each comment requires a minimum length and must belong to one of the predefined services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|min:10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'service'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|in:banos,paseos,cortes'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'service'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After submission, the user is redirected to a simple confirmation view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h2&amp;gt;Comment submitted successfully 🐾&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;Your feedback is very important to us.&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📂 Project Structure
&lt;/h2&gt;

&lt;p&gt;The system follows Laravel’s standard folder organization, keeping logic separated into controllers and models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── Http/Controllers/
│   ├── AuthController.php
│   ├── CommentController.php
│   └── HomeController.php
└── Models/
    ├── Comment.php
    └── User.php
resources/views/
├── layouts/
├── comments/
└── auth/
routes/web.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📚 Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manual authentication&lt;/strong&gt; – Building a login flow from scratch helps you understand Laravel’s core security features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation rules&lt;/strong&gt; – Laravel Validation makes it easy to express complex rules in a clean way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight CAPTCHA&lt;/strong&gt; – Sometimes a simple arithmetic CAPTCHA is all you need for basic security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UX &amp;amp; Accessibility&lt;/strong&gt; – Clear validation messages and confirmations improve usability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity matters&lt;/strong&gt; – Even small projects can teach big lessons about architecture and clean code.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🚀 Next Steps
&lt;/h2&gt;

&lt;p&gt;Some planned improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;strong&gt;unit tests&lt;/strong&gt; using Pest or PHPUnit&lt;/li&gt;
&lt;li&gt;Building an &lt;strong&gt;admin dashboard&lt;/strong&gt; for comment moderation&lt;/li&gt;
&lt;li&gt;Integrating &lt;strong&gt;email notifications&lt;/strong&gt; for confirmation messages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🐶 Conclusion
&lt;/h2&gt;

&lt;p&gt;This project helped me strengthen my skills in authentication, validation, and secure data handling using Laravel.&lt;br&gt;
It’s a reminder that even simple projects can teach a lot about quality, structure, and accessibility.&lt;/p&gt;

&lt;p&gt;👉 You can check the full code here:&lt;br&gt;
🔗 &lt;a href="https://gitlab.com/anaelenaulatesalas/animal_shop" rel="noopener noreferrer"&gt;GitHub – Animal Shop (Pet Shop - Comment System)&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Technical note: The code shown in this article is a simplified version of the real project, with minor variable name and syntax differences, but it preserves the exact same logic and functionality.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>Bulletproof Email Buttons for Outlook: VML + Accessible HTML</title>
      <dc:creator>Ana Elena Ulate Salas</dc:creator>
      <pubDate>Fri, 05 Sep 2025 02:41:34 +0000</pubDate>
      <link>https://dev.to/aulate/bulletproof-email-buttons-for-outlook-vml-accessible-html-4424</link>
      <guid>https://dev.to/aulate/bulletproof-email-buttons-for-outlook-vml-accessible-html-4424</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Email buttons break because Outlook for Windows uses the Word rendering engine. The reliable fix is a hybrid button: VML for Outlook + semantic, accessible HTML/CSS for everyone else. Below you’ll find copy-paste-ready snippets (fixed width &amp;amp; auto width)&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outlook for Windows&lt;/strong&gt; ignores most modern CSS and needs VML shapes to render buttons consistently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: readers using screen readers or high contrast modes need proper roles, labels, and focusable links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: avoid “only the text is clickable” or blue/underlined text in some clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Anatomy of a bulletproof button
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wrapper table&lt;/strong&gt; &lt;code&gt;(role="presentation")&lt;/code&gt;for layout safety.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VML&lt;/strong&gt; &lt;code&gt;&amp;lt;v:roundrect&amp;gt; inside &amp;lt;!--[if mso]&amp;gt; ... &amp;lt;![endif]--&amp;gt;&lt;/code&gt; for Outlook desktop.&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;HTML *&lt;/em&gt; &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; fallback for all other clients with inline styles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: &lt;code&gt;role="button"&lt;/code&gt;, clear link text, &lt;code&gt;aria-label&lt;/code&gt; (optional if text is descriptive), sufficient color contrast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Touch target&lt;/strong&gt;: at least** 44×44px** tappable area (line-height or height + padding).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full clickability&lt;/strong&gt;: &lt;code&gt;display:inline-block&lt;/code&gt; (or &lt;code&gt;block&lt;/code&gt;) and no nested conflicting links.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Paste-ready: Fixed-width button (easiest)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Button : BEGIN --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"presentation"&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;cellspacing=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;cellpadding=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;align=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;align=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
      &amp;lt;v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
                   xmlns:w="urn:schemas-microsoft-com:office:word"
                   href="https://example.com"
                   style="height:48px;v-text-anchor:middle;width:240px;"
                   arcsize="12%"
                   fillcolor="#1268FB"
                   strokecolor="#1268FB"&amp;gt;
        &amp;lt;w:anchorlock/&amp;gt;
        &amp;lt;center style="color:#ffffff;font-family:Arial,Helvetica,sans-serif;font-size:16px;font-weight:bold;"&amp;gt;
          Get started
        &amp;lt;/center&amp;gt;
      &amp;lt;/v:roundrect&amp;gt;
      &amp;lt;![endif]--&amp;gt;&lt;/span&gt;

      &lt;span class="c"&gt;&amp;lt;!--[if !mso]&amp;gt;&amp;lt;!-- --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt;
         &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
         &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
         &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-color:#1268FB;border:1px solid #1268FB;border-radius:6px;
                color:#ffffff;display:inline-block;font-family:Arial,Helvetica,sans-serif;
                font-size:16px;font-weight:bold;line-height:48px;text-align:center;
                text-decoration:none;width:240px;-webkit-text-size-adjust:none;mso-hide:all;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Get started
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!--&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Button : END --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outlook uses the VML rectangle (full-area clickable).&lt;/li&gt;
&lt;li&gt;Other clients render the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; with a fixed width and 48px line-height (meets the 44px touch target).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Paste-ready: Auto-width (padding-based) button
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Button (auto width) : BEGIN --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"presentation"&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;cellspacing=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;cellpadding=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;align=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;align=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
      &amp;lt;v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
                   href="https://example.com"
                   style="height:48px;v-text-anchor:middle;width:200px;"
                   arcsize="12%"
                   fillcolor="#0B5BD3"
                   strokecolor="#0B5BD3"&amp;gt;
        &amp;lt;w:anchorlock/&amp;gt;
        &amp;lt;center style="color:#ffffff;font-family:Arial,Helvetica,sans-serif;font-size:16px;font-weight:bold;"&amp;gt;
          View details
        &amp;lt;/center&amp;gt;
      &amp;lt;/v:roundrect&amp;gt;
      &amp;lt;![endif]--&amp;gt;&lt;/span&gt;

      &lt;span class="c"&gt;&amp;lt;!--[if !mso]&amp;gt;&amp;lt;!-- --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt;
         &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
         &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
         &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"View details"&lt;/span&gt;
         &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-color:#0B5BD3;border:1px solid #0B5BD3;border-radius:6px;
                color:#ffffff;display:inline-block;font-family:Arial,Helvetica,sans-serif;
                font-size:16px;font-weight:bold;line-height:48px;text-align:center;
                text-decoration:none;padding:0 24px;-webkit-text-size-adjust:none;mso-hide:all;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        View details
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!--&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Button (auto width) : END --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Outlook (VML) we must still set a width; pick a reasonable default (e.g., 200–240px).&lt;/li&gt;
&lt;li&gt;Non-Outlook clients will expand with padding: 0 24px.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessibility checklist (quick)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Color contrast&lt;/strong&gt; ≥ 4.5:1 (text vs background).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meaningful text&lt;/strong&gt;: avoid “Click here”.&lt;/li&gt;
&lt;li&gt;role="button" helps screen readers interpret intent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus order&lt;/strong&gt;: keep CTAs in a logical reading order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No image-only buttons&lt;/strong&gt;; if you must, add alt text and a real text link nearby.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dark mode &amp;amp; link styling tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep inline &lt;code&gt;color:#ffffff; text-decoration:none;&lt;/code&gt; on the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Avoid relying on background images.&lt;/li&gt;
&lt;li&gt;Test in Outlook dark mode (recent versions may invert colors; prefer solid brand colors with sufficient contrast).&lt;/li&gt;
&lt;li&gt;To prevent iOS auto-link styling inside buttons, ensure your &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; has explicit &lt;code&gt;color&lt;/code&gt; and `&lt;code&gt;text-decoration:none.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common pitfalls (and fixes)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only text is clickable&lt;/strong&gt; → Ensure the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; is &lt;code&gt;display:inline-block&lt;/code&gt; (or &lt;code&gt;block&lt;/code&gt;) and not wrapped by another conflicting &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extra underlines in Gmail&lt;/strong&gt; → Add &lt;code&gt;text-decoration:none;&lt;/code&gt; inline on the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Font shrinks on mobile&lt;/strong&gt; → Add &lt;code&gt;-webkit-text-size-adjust:none;&lt;/code&gt; inline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rounded corners not showing in Outlook&lt;/strong&gt; → &lt;em&gt;That’s normal&lt;/em&gt;; Outlook uses the VML shape for rounding.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>emaildevelopment</category>
      <category>email</category>
    </item>
  </channel>
</rss>
