made with love,
From Sicily, with my mother's
recipes.
Antonietta cooks the food she grew up on in Sicily — slow-braised
ragù, hand-shaped arancini, the rotating specials her regulars come
back for. No shortcuts, no compromises, and certainly no red-and-white
checkered tablecloths.
The food has a name. So does the woman who makes it.
— Antonietta Benigno
Antonietta Benigno grew up in southern Sicily, where Sunday meant
her mother at the stove and a kitchen full of people. She built a
career on two continents — managing a restaurant in New Jersey,
then teaching back in Italy — before crossing the ocean again in
2021 to be near her daughter in Portland.
She wasn't planning on cooking for a living. She was working as a
manager here when an accident forced her to leave the job — so she
started cooking the way her mother taught her instead, and people
noticed. Her Spanish-speaking co-workers used to call her warmly{' '}
la señora — she translated it into Italian, and made it
the name above her cart.
I'm thinking about the people that will eat my food, and I try to put my heart into it — and they can feel it.
Antonietta · La Signora
Every dish is something her mother taught her: a four-hour ragù
that can't be hurried, arancini shaped one by one, sweets she
learned standing on a chair in a Sicilian kitchen. If she can't
get the right ingredients — some still come from Italy — she
won't put the dish on the board.
Every time I make her recipes, it's like she's right there in the kitchen with me.
)}
);
}
// ── Anatomy / What are arancini ──
function Anatomy() {
return (
A short lesson
/a·ran·CHEE·no/
So, what is an arancino?
A cone of slow-cooked, saffron-touched risotto stuffed with
a savory filling, breaded by hand, and fried to the color of an
Italian sunset. Sicilian street food at its purest — handheld,
warm, and meant to be eaten standing up.
01The shell. A thin, golden crust of toasted breadcrumbs. Audible.
02The rice. Risotto, cooked slowly, kissed with saffron, cooled, shaped.
03The heart. Ragù. Pistachio. Mushroom. The reason you came.
04The name.Arancino or arancina? Sicilians have been arguing about this for centuries. Antonietta makes them in every shape — and calls them whatever she likes.
);
}
function Find() {
const [i, setI] = useState(0);
const [cardMinH, setCardMinH] = useState(0);
const measureRef = useRef(null);
// Measure the tallest info-card across all locations so the grid height
// is locked and doesn't jump between locations.
React.useLayoutEffect(() => {
const el = measureRef.current;
if (!el) return;
const measure = () => {
const cards = el.querySelectorAll('.info-card');
let max = 0;
cards.forEach((c) => { max = Math.max(max, c.offsetHeight); });
if (max > 0) setCardMinH(max);
};
measure();
const ro = new ResizeObserver(measure);
ro.observe(el);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(measure);
return () => ro.disconnect();
}, []);
const loc = LOCATIONS[i];
const next = () => setI((x) => (x + 1) % LOCATIONS.length);
const prev = () => setI((x) => (x - 1 + LOCATIONS.length) % LOCATIONS.length);
const minH = cardMinH || undefined;
return (
Come hungry
Find la Signora.
Three places, one Sicilian mother. The cart, the market, and a café shelf full of frozen Sunday lunch.
{LOCATIONS.map((l, k) => (
))}
{/* hidden measurement layer — renders every location's info-card
inside the same grid template so each gets the correct column
width and we can pick the tallest one */}
{LOCATIONS.map((l) => (
))}
{LOCATIONS.map((_, k) => (
))}
);
}
// ── Reviews ──
const REVIEWS = [
{
quote: 'My Sicilian family and I grew up with the best Italian food in CT, and it\'s been so hard to find anything close out here. Signora Antonietta knows how to make arancini right — the ragù was exactly the real deal. 10/10, would recommend.',
name: 'Vanessa S.',
where: 'Local Guide · July 2026',
via: 'Google',
stars: 5,
},
{
quote: 'La Signora feels like a little piece of home in Portland. Whenever I miss the flavors of Sicily, this is my go-to. Amazing arancini, lasagna, and cannoli — authentic, comforting, and always worth it.',
name: 'Samuele B.',
where: 'June 2026',
via: 'Google',
stars: 5,
},
{
quote: 'When I was pregnant I was always craving arancini — if I\'d known there was such an authentic place making them, I\'d have gone every day! Absolutely delicious and delicate, and finally somewhere that doesn\'t overwhelm the food with spices or garlic. The cannoli are very good too: the shell is crispy, which means they\'re fresh.',
name: 'Raffaella G.',
where: 'Local Guide · June 2026',
via: 'Google',
stars: 5,
},
{
quote: 'Came here on a whim and the cannoli are unreal. Perfect crunch, creamy filling, just the right sweetness. Easily some of the best I\'ve had.',
name: 'Michael D.',
where: 'May 2026',
via: 'Google',
stars: 5,
},
{
quote: 'Delizioso! Un\'oasi della cucina Siciliana a Portland. We shared three types of arancini and almond granita — all delicious and well made. She even helped us practice our Italian. Highly recommend!',
name: 'Katherine S.',
where: 'June 2026',
via: 'Google',
stars: 5,
},
{
quote: 'As someone who\'s half Sicilian, it\'s amazing to see arancino on the menu! So many flavors to choose from — go support them. I\'m saving the cannolo for later.',
name: 'Jeremy F.',
where: 'June 2026',
via: 'Google',
stars: 5,
},
];
function Stars({ n = 5, color = 'var(--saffron)' }) {
return (
{Array.from({ length: 5 }).map((_, i) => (
))}
);
}
function getPerPage() {
if (window.innerWidth <= 680) return 1;
if (window.innerWidth <= 980) return 2;
return 3;
}
function ReviewsSlider() {
const [perPage, setPerPage] = useState(() => getPerPage());
const pages = [];
for (let i = 0; i < REVIEWS.length; i += perPage) {
pages.push(REVIEWS.slice(i, i + perPage));
}
const [page, setPage] = useState(0);
const [trackMinH, setTrackMinH] = useState(0);
const measureRef = useRef(null);
// Update perPage on resize and reset to page 0 to avoid out-of-bounds
useEffect(() => {
const onResize = () => setPerPage(getPerPage());
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
useEffect(() => { setPage(0); }, [perPage]);
// Measure the tallest review-card across ALL reviews at the current
// breakpoint so the slider height never changes between pages.
React.useLayoutEffect(() => {
const el = measureRef.current;
if (!el) return;
const measure = () => {
const cards = [...el.querySelectorAll('.review-card')];
let max = 0;
for (let i = 0; i < cards.length; i += perPage) {
const slice = cards.slice(i, i + perPage);
const sliceMax = slice.reduce((m, c) => Math.max(m, c.offsetHeight), 0);
max = Math.max(max, sliceMax);
}
if (max > 0) setTrackMinH(max);
};
measure();
const ro = new ResizeObserver(measure);
ro.observe(el);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(measure);
return () => ro.disconnect();
}, [perPage]);
const prev = () => setPage((x) => (x - 1 + pages.length) % pages.length);
const next = () => setPage((x) => (x + 1) % pages.length);
const cur = pages[page] || pages[0];
return (
{cur.map((r, i) => (
“
{r.quote}
))}
{/* pad to keep grid columns even when last page is short */}
{cur.length < perPage && Array.from({ length: perPage - cur.length }).map((_, i) => (
))}
{/* hidden measurement layer — renders every review at the current grid
width so we can pick the tallest card to lock the slider height */}
{REVIEWS.map((r, i) => (
“
{r.quote}
))}
{pages.map((_, k) => (
))}
);
}
// Real aggregate from La Signora's Google Business listing (not the
// featured subset below) — keep in sync with the live Google rating.
const RATING = '4.6';
const REVIEW_COUNT = '20+';
function Reviews() {
return (
From her regulars
They keep coming back.
{RATING}/ 5
From {REVIEW_COUNT} reviews on Google.
);
}
const OFFERINGS = [
{
n: '01',
badge: 'Private events',
title: 'Birthdays,\nanniversaries,\nsupper clubs.',
blurb: 'A full Sicilian table for 6 to 40, brought to your kitchen or your backyard. We handle the cooking and the cleanup — you pour the wine.',
bullets: ['6 — 40 guests', 'Cooked on-site or pre-plated', 'Full setup & cleanup'],
palette: ['#B5482B', '#8C341E', '#E0A638'],
},
{
n: '02',
badge: 'Corporate events',
title: 'Arancino stations,\nteam lunches,\nholiday parties.',
blurb: 'An arancino station at your launch, a lasagna lunch for the engineering team, hand-fried cannoli for the holiday party. We bring the cart, the smile, and the food.',
bullets: ['Office lunches & launches', 'Cart pop-ups on-site', 'Cannoli & espresso stations'],
palette: ['#E0A638', '#B07A2D', '#5C6B33'],
},
{
n: '03',
badge: 'Wholesale supply',
title: 'Frozen trays for\ncafés, delis,\nbottle shops.',
blurb: 'Frozen arancini and lasagna trays for cafés, delis, and bottle shops across the Portland-Vancouver metro. Small minimums, weekly drops.',
bullets: ['Portland-Vancouver metro', 'Small minimums', 'Weekly drops'],
palette: ['#5C6B33', '#3F4A21', '#E6D2A9'],
},
];
function Catering() {
return (
Catering & events
Have her cook for your table.
Out of arancini? Let la Signora come to you. Private dinners,
corporate pop-ups, wholesale trays — same hands, same recipes —
one kitchen (or one entire conference floor) full of people
you love.
);
}
// ── Contact ──
const TOPICS = [
{ v: 'catering', label: 'Catering or events' },
{ v: 'wholesale', label: 'Wholesale supply' },
{ v: 'restaurant', label: 'Restaurant updates' },
{ v: 'media', label: 'Press / media' },
{ v: 'hello', label: 'Just saying ciao' },
];
const CHANNELS = [
{ key: 'instagram', label: 'Instagram', handle: '@la_signora_wa', href: 'https://www.instagram.com/la_signora_wa/', Icon: IgIcon },
{ key: 'tiktok', label: 'TikTok', handle: '@la_signora_wa', href: 'https://www.tiktok.com/@la_signora_wa', Icon: TikTokIcon },
{ key: 'facebook', label: 'Facebook', handle: 'La Signora', href: 'https://www.facebook.com/LaSignoraItalianGoodFood/', Icon: FbIcon },
{ key: 'email', label: 'Email', handle: 'info@lasignorafood.com', href: 'mailto:info@lasignorafood.com', Icon: MailIcon },
];
// reCAPTCHA v2 site key — set in each page's as `window.RECAPTCHA_SITE_KEY`.
// When empty the widget is skipped and the form falls back to the server-side
// honeypot + rate limit (see contact-handler.php).
const RECAPTCHA_SITE_KEY =
(typeof window !== 'undefined' && window.RECAPTCHA_SITE_KEY) || '';
function Contact() {
const [form, setForm] = useState({ topic: 'catering', name: '', email: '', message: '', company: '' });
const [errors, setErrors] = useState({});
const [sent, setSent] = useState(false);
const [sending, setSending] = useState(false);
const [submitError, setSubmitError] = useState('');
const recaptchaRef = useRef(null);
const widgetIdRef = useRef(null);
const set = (k, v) => {
setForm((f) => ({ ...f, [k]: v }));
setErrors((e) => ({ ...e, [k]: undefined }));
};
// Render the reCAPTCHA widget once the API is ready. The container only
// exists while the form is shown, so re-render after a reset (sent → false).
useEffect(() => {
if (sent) { widgetIdRef.current = null; return; }
if (!RECAPTCHA_SITE_KEY) return;
let cancelled = false;
let timer;
const render = () => {
if (cancelled) return;
const el = recaptchaRef.current;
const gre = window.grecaptcha;
if (gre && gre.render && el && widgetIdRef.current === null && el.childElementCount === 0) {
widgetIdRef.current = gre.render(el, { sitekey: RECAPTCHA_SITE_KEY });
return;
}
timer = setTimeout(render, 300);
};
render();
return () => { cancelled = true; clearTimeout(timer); };
}, [sent]);
const resetCaptcha = () => {
if (window.grecaptcha && widgetIdRef.current !== null) {
window.grecaptcha.reset(widgetIdRef.current);
}
};
const submit = async (e) => {
e.preventDefault();
const errs = {};
if (!form.name.trim()) errs.name = 'Please share your name.';
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim())) errs.email = 'A real email, per favore.';
if (!form.message.trim() || form.message.trim().length < 10) errs.message = 'Tell us a little more.';
setErrors(errs);
if (Object.keys(errs).length > 0) return;
// reCAPTCHA — require a token before we bother the server.
let captchaToken = '';
if (RECAPTCHA_SITE_KEY) {
captchaToken = (window.grecaptcha && widgetIdRef.current !== null)
? window.grecaptcha.getResponse(widgetIdRef.current)
: '';
if (!captchaToken) {
setSubmitError('Please confirm you’re human with the CAPTCHA below.');
return;
}
}
setSending(true);
setSubmitError('');
try {
const payload = new URLSearchParams(form);
if (captchaToken) payload.set('g-recaptcha-response', captchaToken);
const res = await fetch('contact-handler.php', {
method: 'POST',
body: payload,
});
const data = await res.json();
if (!res.ok || !data.success) throw new Error(data.error || 'send failed');
setSent(true);
} catch {
setSubmitError('Something went wrong sending your message. Please try again, or email info@lasignorafood.com directly.');
resetCaptcha();
} finally {
setSending(false);
}
};
const reset = () => {
setForm({ topic: 'catering', name: '', email: '', message: '', company: '' });
setErrors({});
setSent(false);
setSubmitError('');
};
const topicLabel = TOPICS.find((t) => t.v === form.topic)?.label || '';
return (
Write to la Signora
Tell us what brings you here.
Catering, wholesale, a question about Sunday's lasagna — or
maybe you just want to say ciao. Antonietta reads
everything herself. (Yes, even the long ones.)
La Signora respects your privacy. This policy explains what information we collect, why we collect it, and how we protect it.
Information We Collect
Contact form
When you submit our contact form, we collect your name, email, topic, and message. This is used only to respond to your inquiry.
IP address
We hash your IP address temporarily to prevent form spam. This hash is deleted after one hour.
Google Fonts
We load fonts from Google Fonts. Google may collect your browser type and IP address. See their privacy policy.
reCAPTCHA
Our contact form uses Google reCAPTCHA v2 to verify you are human. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
How We Use Your Information
To respond to your message
To prevent spam and abuse
To deliver a working website
Data Retention
Contact messages: retained indefinitely
Rate-limit hashes: deleted after 1 hour
Google data: retained per their policies
Third Parties
We do not sell your information. Google Fonts and reCAPTCHA may collect data per their policies. We do not use analytics or advertising cookies.
Security
We use HTTPS encryption, validate all submissions, and implement rate-limiting and bot detection.
Your Rights
You can request access, correction, or deletion of your data. Contact us at info@lasignorafood.com.
Changes
We may update this policy. Your use of the site means you accept any changes.
La Signora is committed to making this website usable by everyone, including people who use assistive technology such as screen readers, switch devices, or keyboard-only navigation. We aim to meet the Web Content Accessibility Guidelines (WCAG) 2.1 at Level AA.
What We've Done
Keyboard navigation for the menu, mobile navigation drawer, and photo lightbox, including a "skip to content" link and visible focus states
Descriptive alt text on meaningful images, and decorative icons hidden from screen readers
Proper heading structure and landmarks (navigation, main content, footer)
Color combinations checked against WCAG contrast requirements
Respect for the "reduce motion" setting on animations
Ongoing Work
Accessibility is an ongoing effort, not a one-time fix. We review the site as we add new features and welcome feedback on anything that's hard to use.
Report a Problem
If you encounter an accessibility barrier on this website, please let us know — include the page and what happened, and we'll work to fix it. Contact us at info@lasignorafood.com.