Freeform Freeform for Craft

Formatting Templates

Foundation 6 Improved in 5.0+

The following example assumes you're including necessary Foundation 6 JS and CSS. You can place the additional CSS and JS inside the formatting template or add to your site's CSS / JS files.

Preview

Foundation 6 Example

Video: Preview of Formatting Template Examples

Templates

/foundation-6/
{# Pull in CSS and field rendering #}
<style>
{% include "freeform/_templates/formatting/foundation-6/_main.css" %}
</style>
{% import "freeform/_templates/formatting/foundation-6/_row.twig" as rowMacro %}

{# Render the opening form tag #}
{{ form.renderTag({
    attributes: {
        row: { class: "grid-x grid-margin-x" },
        success: { class: "callout success" },
        errors: { class: "callout alert" },
    },
    buttons: {
        attributes: {
            container: { class: "cell submit-buttons" },
            submit: { class: "button primary" },
            back: { class: "button secondary" },
            save: { class: "button primary" },
        },
    },
    fields: {
        "@global": {
            attributes: {
                container: { class: "cell" },
                label: { class: "freeform-label" },
                input: {
                    novalidate: true,
                    class: "freeform-field",
                },
                instructions: { class: "help-text" },
                error: { class: "no-bullet freeform-errors" },
            },
        },
        ":required": {
            attributes: {
                label: { "+class": "required" },
            },
        },
        ":errors": {
            attributes: {
                input: { "+class": "has-errors" },
            },
        },
        "@group": {
            attributes: {
                label: { "+class": "h6" },
            },
        },
        "@signature": {
            attributes: {
                input: { "+class": "button small secondary" },
            },
        },
    },
}) }}

{# Pull in JS overrides #}
<script>
{% include "freeform/_templates/formatting/foundation-6/_main.js" %}
</script>

{# Success and error message handling for non-AJAX forms #}
{% if not form.settings.ajax %}
    {% if form.submittedSuccessfully %}
        <div{{ form.attributes.success }}>
            <p>{{ form.settings.successMessage | t('freeform') }}</p>
        </div>
    {% endif %}
    {% if form.hasErrors %}
        <div{{ form.attributes.errors }}>
            <p>{{ form.settings.errorMessage | t('freeform') }}</p>

            {% if form.errors|length %}
                <ul class="mb-0">
                    {% for error in form.errors %}
                        <li>{{ error }}</li>
                    {% endfor %}
                </ul>
            {% endif %}
        </div>
    {% endif %}
{% endif %}

{# Render page tabs if multi-page #}
{% if form.pages|length > 1 %}
    <ul class="menu freeform-page-tabs">
        {% for page in form.pages %}
            <li class="menu-text{{ form.currentPage.index == page.index ? ' is-active' : '' }}">
                {{ page.label }}
            </li>
        {% endfor %}
    </ul>
{% endif %}

{# Display form field rows and columns #}
{{ rowMacro.render(form.rows, form) }}

{# Render the closing form tag #}
{{ form.renderClosingTag }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
{% macro getFieldTemplate(type) -%}
    {% set fieldTemplatePath = "freeform/_templates/formatting/foundation-6/fields/" %}
    {{- fieldTemplatePath ~ type ~ ".twig" -}}
{%- endmacro %}

{% macro render(rows, form) %}
    {% import _self as self %}

    <div class="grid-container">

    {% for row in rows %}

        {% set width = (12 / (row|length)) %}

        <div{{ form.attributes.row }}>
            {% for field in row %}

                {% do field.setParameters({
                    attributes: {
                        container: { class: [
                            "medium-" ~ width,
                            "freeform-fieldtype-" ~ field.type,
                        ]},
                    }
                }) %}

                {% include [self.getFieldTemplate(field.type), self.getFieldTemplate("_default")] %}

            {% endfor %}
        </div>

    {% endfor %}

    </div>

{% endmacro %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
button[type=submit].freeform-processing {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: center;
}
button[type=submit].freeform-processing:before {
    content: "";
    display: block;
    flex: 1 0 11px;
    width: 11px;
    height: 11px;
    margin-right: 10px;
    border-style: solid;
    border-width: 2px;
    border-color: transparent transparent #fff #fff;
    border-radius: 50%;
    animation: freeform-processing .5s linear infinite;
}
@keyframes freeform-processing {
    0% {
        transform: rotate(0);
    }
    100% {
        transform: rotate(1turn);
    }
}

.cell > label:first-child,
.freeform-fieldtype-checkbox > label {
    font-weight: 600;
    margin-bottom: 2px;
}
input,
textarea,
select {
    margin-bottom: 0 !important;
}
.input-group-one-line label {
    display: inline-block;
    margin: 0 15px 0 0;
}
input[type="radio"],
input[type="checkbox"] {
    margin: 0 7px 0 0;
    padding: 0;
}
input[type="radio"] + label,
input[type="checkbox"] + label {
    margin: 0;
    padding: 0;
}
label.required:after {
    content: "*";
    margin-left: 3px;
    color: red;
}
.freeform-fieldtype-group > .grid-container {
    margin: -7px -15px -2px;
}
.freeform-table-button-green {
    background: green;
}
.freeform-table-button-red {
    background: red;
}

.freeform-page-tabs {
    border-bottom: 1px solid gray;
    margin-bottom: 15px;
}
.freeform-page-tabs li {
    font-weight: normal !important;
    color: gray !important;
}
.freeform-page-tabs li.is-active {
    font-weight: bold !important;
    color: black !important;
    border: 1px solid gray;
    border-bottom: none;
    background: lightgray;
}
.grid-margin-x {
    padding: 7px 0;
}
.errors {
    color: red;
    margin-bottom: 0 !important;
}
.help-text {
    color: gray;
    line-height: 1.8;
    margin-top: -8px;
    margin-bottom: 2px;
}
.has-error {
    border: 1px solid red;
}
.submit-buttons {
    margin-top: 20px !important;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
var form = document.querySelector('[data-id="{{ form.anchor }}"]');
if (form) {
    // Styling for AJAX responses
    form.addEventListener("freeform-ready", function (event) {
        var freeform = event.freeform;
        freeform.setOption("errorClassBanner", ["callout", "alert"]);
        freeform.setOption("errorClassList", ["errors"]);
        freeform.setOption("errorClassField", "has-error");
        freeform.setOption("successClassBanner", ["callout", "success"]);
    });
    // Styling for Stripe Payments field
    form.addEventListener("freeform-stripe-appearance", function (event) {
        event.elementOptions.appearance = Object.assign(
            event.elementOptions.appearance,
            {
                variables: {
                    colorPrimary: "#0d6efd",
                },
            }
        );
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fields/
/foundation-6/fields/
{{ field.render }}
1
<div{{ field.attributes.container }}>

{{ field.renderInput -}}
{{- field.renderLabel }}
{{ field.renderInstructions }}
{{ field.renderErrors }}

</div>
1
2
3
4
5
6
7
8
{% import "freeform/_templates/formatting/foundation-6/_row.twig" as rowMacro %}

<div{{ field.attributes.container }}>
    {{ field.renderLabel }}
    {{ field.renderInstructions }}

    {{ rowMacro.render(field.layout, form) }}
</div>
1
2
3
4
5
6
7
8
{% set tableColumnCount = field.tableLayout|length %}

{{ field.render({
    addButtonLabel: "Add +",
    removeButtonLabel: "x",
    tableAttributes: {
        table: { class: "table-field" },
        row: { class: "table-row" },
        column: { class: "table-column" },
        label: { class: "table-heading-label" },
        input: { class: "table-input" },
        dropdown: { class: "table-select" },
        checkbox: { class: "table-checkbox" },
        removeButton: { class: "button small alert" },
        addButton: { class: "button small success" },
    },
}) }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

The following CDN links for Foundation are for v6.8.1, which may no longer be the latest version. Please see official Foundation documentation for latest versions and CDN links.

<!-- Compressed CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/css/foundation.min.css" crossorigin="anonymous">

<!-- Compressed JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/js/foundation.min.js" crossorigin="anonymous"></script>
1
2
3
4
5