<?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: Anna Aitchison</title>
    <description>The latest articles on DEV Community by Anna Aitchison (@ara225).</description>
    <link>https://dev.to/ara225</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%2F433686%2F3ea00bcb-30e8-48aa-af9d-334f8d110b47.png</url>
      <title>DEV Community: Anna Aitchison</title>
      <link>https://dev.to/ara225</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ara225"/>
    <language>en</language>
    <item>
      <title>Starting Your Public Speaking Journey</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Fri, 03 Jan 2025 00:18:15 +0000</pubDate>
      <link>https://dev.to/ara225/a-guide-to-starting-your-public-speaking-journey-2eg4</link>
      <guid>https://dev.to/ara225/a-guide-to-starting-your-public-speaking-journey-2eg4</guid>
      <description>&lt;p&gt;I'm a strong believer that public speaking is something that pretty much anyone can do while benefiting both themselves and their community, at least if they happen to work in tech.&lt;/p&gt;

&lt;p&gt;That’s not to say it isn’t hard. It’s a whole new skillset, but if you put the work in there are plenty of opportunities. After all, I’m a shy chaotic Autistic nobody with odd ideas and not especially brilliant speaking skills, and I’ve had plenty of opportunities.&lt;/p&gt;

&lt;p&gt;This is my (somewhat oversized) guide to getting started, filled with useful tidbits from experience. It's based mainly on my experience as an IT professional in the UK speaking as a hobby. Take it with a pinch of salt if you aren’t in a similar boat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Am I?
&lt;/h2&gt;

&lt;p&gt;I’m an IT professional with about five years’ experience. My skillset is varied, with an eclectic mix of sysadmin, cloud engineering, and programming.&lt;/p&gt;

&lt;p&gt;I’ve spoken at seven conferences (four in the UK plus three abroad in Poland, Italy and Japan) as well as ten meetups. Topics range from Kubernetes architecture to having fun on AWS. I’ve done panels and lightning talks as well as standard talks.&lt;/p&gt;

&lt;p&gt;I started by accident. I volunteered to help organize a CodeYourFuture event for International Women’s Day in 2023. We were short on speakers, so I volunteered to share my career journey. I enjoyed it a lot, especially the impact it had on the audience, so I wanted to continue.&lt;/p&gt;

&lt;p&gt;I didn’t have a big network, a prestigious employer or any practical employer support, so I mostly figured out my next steps by trial and error. I’d consider myself fairly expert on application processes etc, but I’m still iterating on the actual speaking side of things.&lt;/p&gt;

&lt;p&gt;I occasionally get invited to speak at meetups, but I mostly apply for opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;I think it’s wise to start by understanding why you want to start speaking. This will shape the topics you want to speak about, and help to give you motivation when you make mistakes or get rejected.&lt;/p&gt;

&lt;p&gt;I didn’t have an explicit why to begin with, and that led to a lot of flailing around just throwing random ideas at organizers, taking any opportunity and getting discouraged easily. I did a few talks I didn’t enjoy and rethought. My core motivations are around challenging myself, inspiring others and being creative. So, I now focus on pitching talks that meet these goals and try not to get too distracted by perks or requests.&lt;/p&gt;

&lt;p&gt;Here are some whys that came to mind for me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenging yourself&lt;/strong&gt;&lt;br&gt;
Maybe speaking scares, you and you want to overcome it? This is as good a reason as any. I don’t have much to say on that topic, I am very lucky that I don’t get stage fright. However, I can say that speaking always holds new challenges for you to work through. Seeing yourself grow as a speaker while helping others is deeply satisfying&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Career&lt;/strong&gt;&lt;br&gt;
Two things change when you start speaking: you add a cool talking point to your CV and your network suddenly becomes a lot deeper and wider. When you’ve spoken, suddenly people know you and proactively come up to you to start conversations. You get many small group networking opportunities with other speakers and the organizers. It’s also an excellent opportunity to impress future employers.&lt;/p&gt;

&lt;p&gt;Being terrible at small talk (I’m Autistic and get social anxiety in group situations), this makes all the difference. I’ve expanded my network massively, met fascinating people from all over the world and had several career opportunities come my way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Giving back&lt;/strong&gt;&lt;br&gt;
Public speaking presents a way to give back by sharing the learning you’ve learnt the hard way, cool things you’ve found and inspiring others.&lt;/p&gt;

&lt;p&gt;This is one of the things I love most about public speaking. Every time someone comes up to me and says that I helped them in some small way, I grin like crazy!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passion&lt;/strong&gt;&lt;br&gt;
If you care about something, speaking about it is a lot of fun. It’s a wonderful opportunity to meet other people who also care about it. You can use talks as an excuse to dive deep into a topic that you’re interested in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freebies/Money&lt;/strong&gt;&lt;br&gt;
I mention this because it isn’t necessarily a good reason – it depends on your circumstances. I have lost money overall doing speaking – I’ve only once been paid travel expenses and never been paid to speak, but I see it as an investment in my happiness and career.&lt;/p&gt;

&lt;p&gt;The great majority of tech speakers aren’t getting paid. Most conferences and meetups I’m aware of are community events. This usually means they’re run on a shoestring budget by a small group of incredible and incredibly stressed people. Most don’t cover expenses or only cover them for local speakers. It’s assumed that employers will cover them.&lt;/p&gt;

&lt;p&gt;Free conference tickets are a good perk, if your employer doesn’t cover them. You will also get a lifetime supply of branded T-shirts, socks and bottles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where?
&lt;/h2&gt;

&lt;p&gt;Assuming you don’t have speaking opportunities through your employer, there are two main types of events – local meetups or conferences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meetups&lt;/strong&gt;&lt;br&gt;
If you’re not already aware, meetups are local groups that host events on a regular basis, usually focusing on one broad topic like AWS. This is where I would start. They’re usually a lovely supportive group. Once you’ve tried a few talks you’ll have more confidence, started to develop a style, and got one or more talks that “work”.&lt;/p&gt;

&lt;p&gt;Opportunities are normally easy to get – some meetups have forms on their Meetup or LinkedIn page or do callouts at the end of their events. Failing that, approaching the organizers with an idea at the event can work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conferences&lt;/strong&gt;&lt;br&gt;
I suggest doing at least one or two meetup talks before starting on conferences (if you want to start on conferences at all). This isn’t a hard and fast rule, but I believe it will put you in a position to get the best out of the experience. If you do want to start with conferences, look for conferences that can provide some support to beginner speakers.&lt;/p&gt;

&lt;p&gt;Conference call for papers are usually hosted on Sessionize.com or Papercall.com. However, not all conferences use these, so don’t just look for conferences here.&lt;/p&gt;

&lt;p&gt;Conference applications require at least a title and a short summary. They both need to be engaging, written with care and have good spelling and grammar because this is a competitive process. Try not to use AI to write it for you, or if you do use it at least edit its output. Writing it yourself forces you to really think about the application, and AI work is occasionally obviously AI. Anything that gives off a low effort vibe is probably bad.&lt;/p&gt;

&lt;p&gt;You will get rejected a lot, usually without feedback. This is normal. Remember it isn’t personal; your talk probably just wasn’t what they needed. A coworker who can confirm your applications aren’t stupid can go a long way towards keeping the motivation up.&lt;/p&gt;

&lt;p&gt;International conferences are not especially difficult to get accepted too, they’re just more expensive to travel to and the language barrier can lead to awkwardness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;p&gt;There is no magic topic. There’s an incredible variety of topics out there and a practically infinite range of points of view and ways of framing them. You don’t even need to be an expert on the topic, provided you can find a way to add value to attendees. This is wonderful – it means you almost certainly have something to contribute, but it’s overwhelming too.&lt;/p&gt;

&lt;p&gt;That said there are perfect topics for an individual (ones that they enjoy talking about and progress their goals) and perfect topics for an event (ones that fit the organizer’s vision). I think good talk development requires keeping both things in mind. Your brilliant talk is pointless if it no events want it, and/or you don’t gain value or enjoyment from it.&lt;/p&gt;

&lt;p&gt;Sources of inspiration could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Your personal projects&lt;/em&gt; – For instance I’ve spoken about my experience with making open-source projects and my crazy pointless AWS personal projects&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Interesting stories&lt;/em&gt; – Human stories are always a winner. I’ve done an AWS billing talk based around stories of billing mishaps which always goes down well&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;That thing your coworkers always want you to help them with&lt;/em&gt; - Something that most people don’t really understand (e.g. Kubernetes or DNS), but you do.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Explaining a complex topic you’ve just learned&lt;/em&gt; – These can go either way. Repeating the manual is boring, but mixing in your learning journey, some analogies and a bit of implementation can make for a memorable talk.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Your Unpopular Opinions&lt;/em&gt; – Playing devil’s advocate on a popular topic like microservices can make for an interesting talk.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Your non-technical skills&lt;/em&gt; – Talks covering things like burnout, people skills, career growth etc aren’t the most common choice but they are important and generic enough to find a home in many different meetups and conferences.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Problems you’ve solved at work&lt;/em&gt; – Or even things you’ve broken at work&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Career next steps&lt;/em&gt; – Talks that impress relevant hiring managers, prove your competence, or fit the requirements for prestigious conferences&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Events&lt;/em&gt; – past talks and submission guidelines can offer inspiration. I usually work backwards from these. I’m more than happy to write a new talk when required though, so I doubt this method would be as fun for others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever topic you choose, make sure you have enough material for the slot you’re applying for before applying. There is always some wriggle room to make it shorter (I do this a lot, it’s a habit I can’t quite seem to shake) or to deviate slightly from the application, but both are best avoided if possible.&lt;/p&gt;

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

&lt;p&gt;The most important thing is just to start applying as soon as you have a half decent idea and an event it might be a fit for. Public speaking is not a thing you can get good at purely through studying theory – you need to do it.&lt;/p&gt;

&lt;p&gt;Thanks for sticking with me. I hope this was useful! Either way, questions, suggestions and feedback are welcome. &lt;/p&gt;

</description>
      <category>speaking</category>
      <category>guide</category>
    </item>
    <item>
      <title>Yamlless Infrastructure as Code</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sat, 25 Jun 2022 18:30:11 +0000</pubDate>
      <link>https://dev.to/ara225/cdk-for-when-your-iac-needs-more-real-code-2k56</link>
      <guid>https://dev.to/ara225/cdk-for-when-your-iac-needs-more-real-code-2k56</guid>
      <description>&lt;p&gt;If Yaml had a tech CV, recuiters would kill to place it. Just look at some of projects it's been a key part of across its star studded career:&lt;/p&gt;

&lt;p&gt;✅️ Kubernetes &lt;br&gt;
✅️ AWS CloudFormation &lt;br&gt;
✅️ Ansible &lt;br&gt;
✅️ GitHub Actions &lt;br&gt;
✅️ Azure DevOps &lt;/p&gt;

&lt;p&gt;Or in slightly more sensible terms; pretty much every single DevOps tool has own configuration language, and many are based on YAML. These languages usually express a great deal fairly simply, so the initial learning curve isn't too steep. However, the problem arises when you start trying to express high levels of complexity with them. Simple things that you'd take for granted in a normal programming language like conditional statements, loops, and text operations, may exist in some form, but quality varies a lot,  they aren't necessarily particularly easy to understand or discover, and the result can be an alphabet soup that's near impossible for humans to parse without deep subject knowledge.&lt;/p&gt;

&lt;p&gt;However, at least in Infrastructure as Code, you can actually use general purpose programming languages. This is by no means a cure all but for some people,  like me, it might well be a more pleasant option. There are two options that I'm aware of; AWS Cloud Development Kit (which comes in three variants for: AWS only, Terraform and Kubernetes) and Pulumi, which seems to be Terraform based, not an AWS person, there are other options too; there are variants of it for Kubernetes and Terriform and a completely different project called Pulumi, which seems to be Terraform based. I've honestly only worked with the AWS only version of the CDK, so I'll fcus on that, but the other three options should be much the same. Love to hear from anyone who's used the other versions.&lt;/p&gt;

&lt;p&gt;The way it works internally is really intresting; they wrote a whole framework for translating between languages so they can have just one codebase and support TypeScript, JavaScript, Python, Java, C# or Go with  modules in each programming language, and the end result actually gets written out as CloudFormation Yaml, so you can use that directly if you have to.&lt;/p&gt;

&lt;p&gt;This approach to Infrastructure as Code obviously has its own complexities, and you need to know one of those languages already to get the best out of it. The one problem that I mainly encountered with the CDK  was that the documentation isn't necessarily the best, but IDE completion is available, the code is opensource and the team are super helpful if you raise a GitHub issue. It's not necessarily for everyone, but is well worth checking out if you relate to my little rant about Yaml and friends. &lt;/p&gt;

&lt;p&gt;First blog post in a while, I'm trying to take this a bit more seriously than before so feedback is welcome :)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
      <category>yaml</category>
    </item>
    <item>
      <title>Logging to a Docker Container stdout from a Background Process</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sun, 02 May 2021 17:05:13 +0000</pubDate>
      <link>https://dev.to/ara225/logging-to-a-docker-container-stdout-from-a-background-process-3dkg</link>
      <guid>https://dev.to/ara225/logging-to-a-docker-container-stdout-from-a-background-process-3dkg</guid>
      <description>&lt;p&gt;Background processes in Docker containers can be a bit of a pain for logging, however they can't always be avoided. For instance, I was working on a container for a legacy CGI application at work recently, which required both a FastCGI wrapper and a Nginx webserver in the same container, with Nginx running as the foreground process and logging to the container's stdout. The CGI application does a lot of logging and we didn't really want to have to manage those logs separately from the Nginx logs. The CGI wrapper we were stuck with doesn't pass on stderr (with CGI stdout goes to the browser so that's pointless also) so we had to resort to more devious means.&lt;/p&gt;

&lt;p&gt;Linking a file/logging to /dev/stdout (or /dev/stderr) doesn't work because these are merely references to the current process's stdout/stderr and logging to a terminal device similarly doesn't work because in production, there won't really be a terminal. &lt;/p&gt;

&lt;p&gt;So, I was stuck and gave up on the idea for a bit until I realized that in Linux process can write to other processs' stdin and stderr. There are 'files' in  /proc/TARGET_PROCESS_PID/fd that represent stdin and stderr for that process, and you can write to them just like any other file if you have the right permissions. Processes can only write to these 'files' if they're running under the same user and group as the target process, and if the user is not root, you might have to change the permissions of the 'files' further down the link chain. Of course, in in my situation, the master nginx process was owned by root and the CGI scripts were running under an unprivileged user. However, I worked around this by writing to the stdout of a nginx worker processes, which would then be picked up by the master process. &lt;/p&gt;

&lt;p&gt;Unfortunately, due to the way we set up the container in the end, this solution hasn't found it's way into production, but I hope it's useful to you.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>container</category>
      <category>logging</category>
    </item>
    <item>
      <title>Built my first C#/NuGet package!</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Fri, 22 Jan 2021 23:01:14 +0000</pubDate>
      <link>https://dev.to/ara225/built-my-first-c-nuget-package-4189</link>
      <guid>https://dev.to/ara225/built-my-first-c-nuget-package-4189</guid>
      <description>&lt;p&gt;Yesterday, I pushed &lt;a href="https://www.nuget.org/packages/ara225.DynamoDBUserStore/" rel="noopener noreferrer"&gt;ara225.DynamoDBUserStore&lt;/a&gt; my first ever NuGet package. Basically, it's a wrapper around DynamoDB functions which allows ASP NET Core Identity to store user account details and role information in AWS DynamoDB (otherwise known as a custom user &amp;amp; role store for ASP .NET Core).&lt;br&gt;
It's not glamorous or perfect, but it is somewhat complex and as well engineered and tested as I can make it, so I'm rather proud of it on a technical level. &lt;/p&gt;

&lt;p&gt;It's also a fairly large achievement on a personal level. I used to be scared of more complex langues (namely C. C++, Java, and C#) because I tried and failed to learn C++ as a kid (and promptly gave up programing as a result). But, frankly that was about a decade ago, and I didn't have proper resources or know any developers so it's been an irrational fear for a long time. &lt;/p&gt;

&lt;p&gt;Anyway, three months ago, I decided to throw myself in the deep end with C# by building a ASP .NET core app. I couldn't think of anything good to make, so I made a basic CRUD app (posing as a project management app) with authentication and made some unusual requirements (store everything in DynamoDB being the core one). I ended up making my own user store which became this thing.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>csharp</category>
      <category>nuget</category>
      <category>aws</category>
    </item>
    <item>
      <title>Key AWS Services for Building Serverless Web Apps</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sat, 09 Jan 2021 00:38:38 +0000</pubDate>
      <link>https://dev.to/ara225/key-aws-services-for-building-serverless-web-apps-l3f</link>
      <guid>https://dev.to/ara225/key-aws-services-for-building-serverless-web-apps-l3f</guid>
      <description>&lt;p&gt;Despite the name, serverless isn't about not having servers; it's about abstracting the servers away so that developers and/or operations don't need to think about them. This means having to learn about services and software specific to a cloud provider instead that may not come as naturally to you and aren't as transferable. However, serverless can unlock a lot of power in terms of cost saving, scalability and not having to worry about servers, if used in the right situations. &lt;/p&gt;

&lt;p&gt;In this post, I'm introducing the AWS serverless services most commonly used for building serverless web applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3
&lt;/h3&gt;

&lt;p&gt;The ubiquitous S3, famous for its users' security mess ups, which are actually rather hard to do. It's a file storage solution - you just dump files in a S3 bucket and pay for what you use in terms of data transfer and storage space, based on your chosen storage class (basically defines the speed of access and redundancy of a file). As well as simply storing files, a S3 bucket can trigger workflows on file upload, automatically change the storage class of files, and even serve a static website. &lt;/p&gt;

&lt;h3&gt;
  
  
  Lambda
&lt;/h3&gt;

&lt;p&gt;AWS Lambda allows you to essentially just run functions on demand - triggered by events from other AWS services, or manual execution - and only pay for the time they're actually executing. Lambda functions can be in practically any language: most popular ones are supported out of the box, and support for other languages can be added in.&lt;/p&gt;

&lt;p&gt;The free tier - one million executions per month - is forever, and the pricing, if you exceed that limit is reasonable. It scales seamlessly and virtually infinitely, but does have a limit on how many functions can be running at once. Cold starts can be an issue - because there is a dedicated VM or container underneath, it takes time to start up. AWS does keep the underlying infrastructure up for a while after each execution, at no cost to you, so this is a non-issue if the function is busy enough, and it's a fairly small delay anyway. &lt;/p&gt;

&lt;h3&gt;
  
  
  API Gateway
&lt;/h3&gt;

&lt;p&gt;API Gateway is designed to go in front of other AWS services and enable them to be accessed over HTTPS with straight forward security, caching and routing options. It's often used with Lambda to create a REST API or some sort of webapp. Authentication can be done based on Cognito, API keys, IP address or more.&lt;/p&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;p&gt;DynamoDB is a fast, simple, auto scaling NoSQL database solution. Being a NoSQL DB, though, relational data or even data with lots of primary keys isn't really its strong suit: joins aren't a thing, and you need to manually add secondary indexes if you want to search by multiple primary keys (you can add two out of the box, but they're treated as one), without returning more items than you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFront
&lt;/h3&gt;

&lt;p&gt;CloudFront is a caching service - it caches webpages at edge locations, so that user requests aren't always hitting the place you're serving them from and are served from locations closer to the user. It also lets you use HTTPS with custom SSL certificates so you can get HTTPS on a S3 website.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch
&lt;/h3&gt;

&lt;p&gt;CloudWatch does a lot of things, mostly centred around security, monitoring, alerting and logging. From a serverless application development point of view, the two most useful features are CloudWatch Events, which lets you schedule Lambda executions and the logging - everything that your Lambda functions print to the screen ends up here, so it's invaluable for debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;These are by far not the only serverless services useful for web development. Some of the other major ones are SNS, and SQS which enable asynchronous task processing, Fargate which lets you run containers serverlessly (for a premium), SES which sends emails, other database solutions such as Amazon Aurora Serverless (a serverless SQL database) and Cognito, a user authentication service. There are even services like AWS Amplify that simplify and abstract away the services. However, the core service fill the needs of most basic serverless apps. &lt;/p&gt;

&lt;p&gt;By the way, this post is going to be  part of a series exploring web development with AWS serverless services. So, if you liked it, you might want to follow me. Do be aware that I post all kinds of random stuff though :).&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a User Login System with ASP .NET Core Identity</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Thu, 07 Jan 2021 23:57:27 +0000</pubDate>
      <link>https://dev.to/ara225/building-a-user-login-system-with-asp-net-core-identity-1l2c</link>
      <guid>https://dev.to/ara225/building-a-user-login-system-with-asp-net-core-identity-1l2c</guid>
      <description>&lt;p&gt;The result of bootstraping an ASP .NET Core app with user login is somewhat deceptive: it just works out of the box. In practice, however, ASP .NET Core Identity - the default user login solution for ASP .NET Core is quite complex/difficult to setup for production use cases, as I've found out recently. &lt;/p&gt;

&lt;p&gt;You don't have to do this - Auth0 and others make solutions that cut out much of the work, but if you need or want to, this high level overview of the process with handy links may be useful to you. I'm going to assume that you have a bootstraped ASP .NET Core app with user authentication (individual user accounts stored in app).&lt;/p&gt;

&lt;h1&gt;
  
  
  User Storage
&lt;/h1&gt;

&lt;p&gt;ASP .NET Core Identity allows user storage providers to be swapped out, so that you can store your users anywhere. The bootstraped app is setup to use a local database on disk. It's fairly easy to swap over to a proper MS SQL server using Entity Framework by changing the connection string in the configuration file, however for anything else, you'll need to fall back on community maintained user stores or write your own. Microsoft does have a &lt;a href="https://github.com/dotnet/AspNetCore/tree/master/src/Identity" rel="noopener noreferrer"&gt;handy list of these storage providers&lt;/a&gt; though and a couple of decent pieces of documentation.&lt;/p&gt;

&lt;h1&gt;
  
  
  OAuth Providers
&lt;/h1&gt;

&lt;p&gt;It's fairly simple to add an OAuth provider if you need to. Microsoft provides support out of the box for four (Microsoft, Google, Twitter, Facebook), and provide &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/?view=aspnetcore-5.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;a nice guide&lt;/a&gt;. Aside from this, you can implement your own integration or use a prebuilt integration &lt;a href="https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers" rel="noopener noreferrer"&gt;this repo has a really nice selection.&lt;/a&gt; The buttons to authenticate with the provider(s) will appear on the login page automatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  UI
&lt;/h1&gt;

&lt;p&gt;The UI Microsoft provides works, but it's not pretty. Luckily, it's fairly easy to customize, as it's provided as a Razor Pages library. You can access the code by scaffolding it into your project (&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-5.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;see this Microsoft doc&lt;/a&gt;). You'll probably want to customize the 2fa section of the app to display a QR code for users to scan (&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-enable-qrcodes?view=aspnetcore-5.0" rel="noopener noreferrer"&gt;see this MS doc&lt;/a&gt;)&lt;/p&gt;

&lt;h1&gt;
  
  
  Email
&lt;/h1&gt;

&lt;p&gt;You will need to add a real email sender to your app to send confirmation emails (assuming you want to use them). Microsoft provides &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?view=aspnetcore-5.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;a tutorial for using SendGrid with this&lt;/a&gt; and I wrote &lt;a href="https://dev.to/ara225/how-to-use-aws-ses-as-a-custom-email-sender-in-asp-net-core-identity-40a3"&gt;one for AWS SES.&lt;/a&gt; No matter which provider you're using, it's a fairly simple process.&lt;/p&gt;

</description>
      <category>csharp</category>
    </item>
    <item>
      <title>What's the best way to represent time spent on open source on a CV or Linkedin?</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sun, 03 Jan 2021 11:12:24 +0000</pubDate>
      <link>https://dev.to/ara225/what-s-the-best-way-to-represent-time-spent-on-open-source-on-a-cv-or-linkedin-27hn</link>
      <guid>https://dev.to/ara225/what-s-the-best-way-to-represent-time-spent-on-open-source-on-a-cv-or-linkedin-27hn</guid>
      <description>&lt;p&gt;Personally, I'm spending a significant chunk of time on a regular basis working on open source stuff, so I feel like it might be worth elevating beyond the projects section to the experience section. However, I'm worried it might appear pretentious because I'm mainly working on my own projects, and they're pretty niche so they aren't especially successful or glamorous and definitely don't make me any money. Wondering what others think.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Host a Static Website on AWS with HTTPS and CI/CD</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Thu, 31 Dec 2020 21:46:50 +0000</pubDate>
      <link>https://dev.to/ara225/how-to-host-a-static-website-on-aws-with-https-and-ci-cd-33of</link>
      <guid>https://dev.to/ara225/how-to-host-a-static-website-on-aws-with-https-and-ci-cd-33of</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;S3 is the obvious place to host a static (frontend code only) website on AWS. It's a simple, serverless way to store and serve files without running a server or fiddling with a storage server, scales effortlessly, and is very inexpensive, with a free tier and pay per request modal. &lt;/p&gt;

&lt;p&gt;In theory, all you have to do is dump some files in a S3 bucket, set permissions on the bucket to allow public access and static site hosting, and forward your domain to it with a CNAME DNS value. In practice however, this approach has two issues: S3 buckets by themselves don't support HTTPS, and you need to upload files manually to S3. This article goes over a slightly more advanced setup with CloudFront for caching and HTTPS, and GitHub Actions for CI/CD. &lt;/p&gt;

&lt;p&gt;There are much easier free or virtually free options for hosting static sites such as GitHub pages, but if you want control over your infrastructure, a production website, or a bit of AWS experience to show off, this is a great way to go. &lt;/p&gt;

&lt;h1&gt;
  
  
  Assumptions
&lt;/h1&gt;

&lt;p&gt;This article assumes that you're already setup on AWS, have a domain or subdomain you want to use, and have code in GitHub. &lt;/p&gt;

&lt;h2&gt;
  
  
  S3 Bucket
&lt;/h2&gt;

&lt;p&gt;The files will be stored in a S3 bucket. The name doesn't really matter, but you need to enable static website hosting on the bucket and allow public read access to it. &lt;/p&gt;

&lt;p&gt;First, go to the Properties tab on the S3 bucket's page, and enable static web hosting. Take note of the bucket's website URL. Go to the Permissions tab and click edit under "Block public access (bucket settings)". Untick all the checkboxes and save the changes. Add the following policy to the bucket policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Policy1589309574299"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stmt1589309569196"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_BUCKET_ARN/*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  HTTPS Certificate
&lt;/h2&gt;

&lt;p&gt;Create a HTTPS certificate for your domain or subdomain in the AWS Certificate Manager. You must use the North Virginia AWS region for this certificate to be seen by CloudFront, no matter what region you set your CloudFront distribution up in. If you don't have your domain in AWS Route 53, you'll need to verify that you own the domain/subdomain by setting some DNS records on it. As long as the certifcate is public, which it has to be for this purpose, it's free to create, store and use.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudFront
&lt;/h2&gt;

&lt;p&gt;You also need to create a CloudFront web distribution. Most of the settings don't really matter for this to work, here are the ones that do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Origin Domain Name - CloudFront provides a handy dropdown list, but this fills the field in with the S3 bucket's API URL, which works but doesn't provide automatic redirects from a folder to index.html and lacks a couple of other convenience features. You'll almost always want to use the bucket's static website URL instead (you'll find it under the bucket's Properties tab)&lt;/li&gt;
&lt;li&gt;Origin Path - Leave blank if you want to use all files in the bucket. Asterisks don't work - they're taken literally.&lt;/li&gt;
&lt;li&gt;Alternate Domain Names (CNAMEs) - List the domain names that the distribution will be accessed by&lt;/li&gt;
&lt;li&gt;SSL Certificate - Choose a custom SSL certificate. This choice only becomes active after CloudFront detects a SSL cert in CM in the correct region. Takes some time after it's done to actually register it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DNS
&lt;/h2&gt;

&lt;p&gt;Forward your domain/subdomain to the CloudFront distribution's URL (*.cloudfront.net) with a CNAME DNS entry. If you're not using Route 53, you won't be able to forward the root domain to CloudFront out of the box, but there are a few free services that'll do it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Actions
&lt;/h2&gt;

&lt;p&gt;GitHub Actions are a simple yet effective CI/CD solution integrated right into GitHub. You can find out more &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It's free for public repos and has a decent free trial for others. Most of the work for this action is already done - there's a couple of excellent pre baked actions. I find that &lt;a href="https://github.com/reggionick/s3-deploy" rel="noopener noreferrer"&gt;reggionick/s3-deploy&lt;/a&gt; works the best for this scenario - it removes old files from the S3 bucket, adds new ones and invalidates the CloudFront cache all in one go. You simply need to use the example action in that repo's readme, add, change or remove the build steps, create the needed repository secrets and add the workflow to your repo. You might want to change the trigger to be triggered only on push to the master branch and change the folder (location where the deployable assets are/end up relative to repo root).&lt;/p&gt;

&lt;p&gt;The secrets you need are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY - AWS credentials. Hopefully for a programmatic access only IAM user with only the access needed to list, add and remove objects from the specific S3 bucket and to invalidate the cache on the CloudFront distribution.&lt;/li&gt;
&lt;li&gt;S3_BUCKET - Name of S3 bucket&lt;/li&gt;
&lt;li&gt;S3_BUCKET_REGION - Region S3 bucket was created in&lt;/li&gt;
&lt;li&gt;CLOUDFRONT_DISTRIBUTION_ID - ID of CloudFront distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you add the secrets first and already have code in your repo, when you commit your workflow into the repo, it should run successfully and you'll hopefully have a working website. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>How to Upload files to S3 in a C# ASP .NET Core App</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sat, 19 Dec 2020 17:17:02 +0000</pubDate>
      <link>https://dev.to/ara225/upload-files-to-s3-in-a-c-asp-net-core-app-6go</link>
      <guid>https://dev.to/ara225/upload-files-to-s3-in-a-c-asp-net-core-app-6go</guid>
      <description>&lt;p&gt;The instructions in this article are designed for a C# ASP .NET MVC application. They should work elsewhere are with tweaks for your situation. This assumes that your AWS creds are already setup. It's based on &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0" rel="noopener noreferrer"&gt;a Microsoft article about file uploads&lt;/a&gt; with tweaks for the s3 upload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add dependency
&lt;/h3&gt;

&lt;p&gt;Add the NuGet module AWSSDK.S3 to your project&lt;/p&gt;

&lt;h3&gt;
  
  
  Add the data class
&lt;/h3&gt;

&lt;p&gt;Create a file with this content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon.S3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon.S3.Transfer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;YOUR_NAMESPACE&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;S3Upload&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;RegionEndpoint&lt;/span&gt; &lt;span class="n"&gt;bucketRegion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RegionEndpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EUWest2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IAmazonS3&lt;/span&gt; &lt;span class="n"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UploadFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;keyName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;s3Client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AmazonS3Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketRegion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fileTransferUtility&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransferUtility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;fileTransferUtility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UploadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&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;h3&gt;
  
  
  Add a new modal
&lt;/h3&gt;

&lt;p&gt;Add a new modal called FileUploadFormModal.cs to your project with this content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileUploadFormModal&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"File"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;FormFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&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;h3&gt;
  
  
  Add a view
&lt;/h3&gt;

&lt;p&gt;Create a view called FileUploadForm.cshtml with this content in Views/Home&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@model FileUploadFormModal
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;asp-for=&lt;/span&gt;&lt;span class="s"&gt;"FileUploadFormModal.FormFile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dd&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;asp-for=&lt;/span&gt;&lt;span class="s"&gt;"FileUploadFormModal.FormFile"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;asp-page-handler=&lt;/span&gt;&lt;span class="s"&gt;"Upload"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Upload"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Edit HomeController.cs controller
&lt;/h3&gt;

&lt;p&gt;Add a using statement for the data class and add the following code to the HomeController.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FileUploadForm&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;View&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FileUploadForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileUploadFormModal&lt;/span&gt; &lt;span class="n"&gt;FileUpload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memoryStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;FileUpload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Upload the file if less than 2 MB&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;2097152&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;S3Upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UploadFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"YOUR_BUCKET_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"YOUR_KEY_NAME"&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="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModelError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The file is too large."&lt;/span&gt;&lt;span class="p"&gt;);&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;View&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;



</description>
      <category>csharp</category>
      <category>aws</category>
      <category>s3</category>
    </item>
    <item>
      <title>How to Use AWS SES as a Custom Email Sender in ASP .Net Core Identity</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Wed, 09 Dec 2020 00:13:29 +0000</pubDate>
      <link>https://dev.to/ara225/how-to-use-aws-ses-as-a-custom-email-sender-in-asp-net-core-identity-40a3</link>
      <guid>https://dev.to/ara225/how-to-use-aws-ses-as-a-custom-email-sender-in-asp-net-core-identity-40a3</guid>
      <description>&lt;p&gt;This article goes over the process to use AWS SES as a custom email sender with ASP .Net Core Identity. I'm using this inside a MVC project. &lt;/p&gt;

&lt;p&gt;This code depends on AWSSDK.SimpleEmail (which can be installed from nuget) and Microsoft.AspNetCore.Identity.UI (should be included in the default install of identity).&lt;/p&gt;

&lt;p&gt;The custom email sender needs to implement IEmailSender, which only has one method, SendEmailAsync; apart from this, you can do whatever you like. My implementation below should work fine for you if you can have have AWS credentials set up on the machine as it uses the AWS SDK. Alternatively, you can access SES via SMTP and drop your implementation in. Apart from this, you simply need to add the custom email sender in the ConfigureServices method of Startup.cs. To do this, simply add this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SESEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need to follow the steps in &lt;a href="https://docs.microsoft.com/en-gb/aspnet/core/security/authentication/accconfirm?view=aspnetcore-5.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;this Microsoft article &lt;/a&gt; from Scaffold RegisterConfirmation on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Email Sender implementation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon.SimpleEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Amazon.SimpleEmail.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Identity.UI.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;YOUR_NAMESPACE&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SESEmailSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmailSender&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;receiverAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;htmlBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Change to your from email&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;senderAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sender@example.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Change to your region&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AmazonSimpleEmailServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RegionEndpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EUWest2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sendRequest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendEmailRequest&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;senderAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Destination&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;ToAddresses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;receiverAddress&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;Html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;Charset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htmlBody&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;Charset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htmlBody&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&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;



</description>
      <category>csharp</category>
      <category>aws</category>
      <category>email</category>
    </item>
    <item>
      <title>Project to Index &amp; Search WebFont Icons</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Tue, 27 Oct 2020 21:56:49 +0000</pubDate>
      <link>https://dev.to/ara225/webfont-icon-search-project-49kf</link>
      <guid>https://dev.to/ara225/webfont-icon-search-project-49kf</guid>
      <description>&lt;p&gt;I've made an interesting little project (hosted on a &lt;a href="http://ii.aitchisonsoft.co.uk/" rel="noopener noreferrer"&gt;subdomain of my personal website&lt;/a&gt;). The concept of it is that it's a search engine for icons from web icon packs. &lt;/p&gt;

&lt;p&gt;The indexing is done by running utilities/filterIconPack.py which parses the icon pack's CSS (metadata + links to the CSS are stored in utilities/iconPacks.json) into JSON. &lt;/p&gt;

&lt;p&gt;The frontend is a static site in S3, which makes API calls to a simple AWS lambda + API Gateway backend. The backend searches the icon data JSON file which fuse.js and returns the results.&lt;/p&gt;

&lt;p&gt;It hasn't turned out as useful as I had hoped, since the way it's designed excludes many icon packs and I've failed to find a way of automatically collecting pack metadata. However, it's still a passably interesting project, so I decided to share. &lt;/p&gt;

</description>
      <category>showdev</category>
      <category>aws</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to create a Custom Multi Select Dropdown</title>
      <dc:creator>Anna Aitchison</dc:creator>
      <pubDate>Sun, 11 Oct 2020 20:36:57 +0000</pubDate>
      <link>https://dev.to/ara225/how-to-create-a-custom-multi-select-dropdown-2mnb</link>
      <guid>https://dev.to/ara225/how-to-create-a-custom-multi-select-dropdown-2mnb</guid>
      <description>&lt;p&gt;HTML provides a native way to create a select box which supports selecting multiple items with the multiple attribute on select elements but the rendering of this really isn't very compact or pretty, and there's not much you can do styling wise with options in a select box either. I couldn't find any custom designs that fitted the style I was looking for so I ended up making my own. The full code is available on my GitHub &lt;a href="https://github.com/Ara225/mini-frontend-projects/blob/master/5-custom-form-controls/5-custom-form-controls.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; along with a matching search bar.&lt;/p&gt;

&lt;p&gt;This is built from scratch due to difficulties in changing the behavior and style of a normal select. The normally visible part of the select is a simple button. The dropdown menu is a div hidden by the Bootstrap utility class d-none, with rounding and shadowing applied via the Bootstrap utility classes shadow and rounded. The options are simple checkboxes with labels. These could be replaced with radio buttons if you wanted a matching single select box.&lt;/p&gt;

&lt;p&gt;Here's the final HTML for the button and menu&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"dropDown(event);"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-btn"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Menu 1 &lt;span class="ni"&gt;&amp;amp;#9013;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-none shadow rounded menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-block menu-option"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
                Option 1&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-block menu-option"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
                Option 2&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-block menu-option"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
                Option 3&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted the menu button to be rounded and quite small so I styled it accordingly (you can style any way you wish of course, doesn't affect the function at all):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&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;For the menu itself, I added a little padding so that text wasn't running up against the top of the menu and some margin so it didn't overlap with the button (both completely optional). I also added a high z-index (so that it'd display over other stuff), a background color (default is transparent which looks silly in this context), and set position to absolute so it doesn't push other stuff down the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&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;For the menu options, I added a little padding to separate them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu-option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="m"&gt;6px&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;We need a way to detect when clicks are made outside of the dropdown menu so that it can be dismissed by clicking outside of it like a normal select box. I've done this by creating a div which covers the whole screen with a lower z-index than the menu. This allows us to detect all clicks outside of the menu&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-none"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"overlay"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"hide(event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#overlay&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&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;To make this actually work, we need JavaScript functions to make the menus and overlay appear and disappear.&lt;br&gt;
This function removes the d-none classes from the menu and overlay, activating them. Rather than handling this via ID, it simply takes the second element of the target's (button's) parent and assumes that's the menu. This is the reason that the menu and button are wrapped in an otherwise empty div.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dropDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d-none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;overlay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d-none&lt;/span&gt;&lt;span class="dl"&gt;"&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 function adds the d-none class to the overlay and all elements with the class menu, hiding them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d-none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;overlay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d-none&lt;/span&gt;&lt;span class="dl"&gt;"&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;If you found this useful, you might also like my &lt;a href="https://dev.to/ara225/simple-search-bar-design-286a"&gt;matching search bar design&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
